bash: loop through procces output and terminate the process - linux

I need some help with the following:
I use linux to script commands sent to a device. I need to submit a grep logcat command to the device and then iterate its output as it is being generated and look for a particular string. Once this string is found I want my script to move to the following command.
in pseudocode
for line in "adb shell logcat | grep TestProccess"
do
if "TestProccess test service stopped" in line:
print line
print "TestService finished \n"
break
else:
print line
done

adb shell logcat | grep TestProcess | while read line
do
echo "$line"
if [ "$line" = "TestProces test service stopped" ]
then echo "TestService finished"
break
fi
done

adb shell logcat | grep -Fqm 1 "TestProcess test service stopped" && echo "Test Service finished"
The grep flags:
-F - treat the string literally, not as a regular expression
-q - don't print anything to standard output
-m 1 - stop after the first match
The command after && only executes if grep finds a match. As long as you "know" grep will eventually match and want to unconditionally continue once it returns, just leave off the && ...

You could use an until loop.
adb shell logcat | grep TestProccess | until read line && [[ "$line" =~ "TestProccess test service stopped" ]]; do
echo $line;
done && echo -n "$line\nTestService finished"

Related

How to tail output from adb logcat and execute a command each new line

I'm trying to do very likely to this post but instead of reading from a file I want to "subscribe" to the output of adb logcat and every time a new line is logged I will run some code on this line.
I tried these codes, but none worked
tail -f $(adb logcat) | while read; do
echo $read;
processLine $read;
done
or
adb logcat >> logcat.txt &
tail -f logcat.txt | while read; do
echo $read;
processLine $read;
done
What is the simples way to do this? Thanks in advance
The following two solutions should work. I generally prefer the second form as the wile loop is run in the current process and so I can use local variables. The first form runs the while loop in a child process.
While loop in child process:
#!/bin/bash
adb logcat |
while read -r line; do
echo "${line}"
processLine "${line}"
done
While loop in current process:
#!/bin/bash
while read -r line; do
echo "${line}"
processLine "${line}"
done < <(adb logcat)

Bash script to write all occurrences of application crash to a text file

I can't find how to structure my while loop properly to log to a text file all crashes of a given Linux application. I would like to get a prompt so I can input the application name and then a loop to watch the pid of the application. If the pid is null I wanted to log the timestamp in a text file and continue the loop. If still in null at the second iteration, don't log anything, keep monitoring until there are other crashes and other logs... and so on until the script stops with CTRL+C.
I've tried multiple variations of this script without luck. I think I need tips on how to think of a "loop structure" to achieve whatever goals...
read -p "What is the name of the application you want to monitor?" appname
pidofapp=`ps -elf | grep "$appname" | grep -v grep | awk '{print $4}'`
pidofappcheck=`ps -elf | grep "$appname" | grep -v grep | awk '{print $4}'`
while :
do
if [[ ! -z "$pidofappcheck" ]] ; then
pidwasnull=true
pidofappcheck=`ps -elf | grep "$appname" | grep -v grep | awk '{print $4}'`
if [[ "$pidofapp" == "$pidofappcheck" ]] ; then
printf "Still monitoring...\n"
sleep 5
elif [[ "$pidwasnull" == true ]] ; then
continue
fi
echo "FAILURE: Crash occurred at: "$(date)" - Crashes will be logged in the monitoring tool directory under results.txt"
date >> ./results.txt
fi
done
As it is right now, the script will echo:
What is the name of the application you want to monitor?running
Still monitoring...
FAILURE: Crash occurred at: Wed May 22 01:44:53 EDT 2019 - Crashes will be logged in the monitoring tool directory under results.txt
Still monitoring...
FAILURE: Crash occurred at: Wed May 22 01:44:58 EDT 2019 - Crashes will be logged in the monitoring tool directory under results.txt
Thanks in advance for any help.
Try something like this
#!/bin/bash
getpidofapp() {
# pid=$(ps -elf | grep "$1" | grep -v grep | awk '{print $4}' | head -1)
pid=$(pgrep "$1" | head -1)
test -n "${pid}" || { echo "Is ${appname} running?"; exit 1; }
}
read -rp "What is the name of the application you want to monitor?" appname
app_pid=$(getpidofapp "${appname}")
while : ; do
lastpid=$(getpidofapp "${appname}")
if [[ "${app_pid}" == "${lastpid}" ]] ; then
printf "Still monitoring...\n"
else
crashtxt="Crashes will be logged in the monitoring tool directory under results.txt"
echo "FAILURE: Crash occurred at: $(date) ${crashtxt}"
date >> ./results.txt
fi
sleep 5
done
So I have been able to find a solution based on what #Walter A wrote. Here is what I've used. It's working as expected so far.
#!/bin/bash
read -rp "What is the name of the application you want to monitor?" appname
app_pid=$(pidof "$appname")
#echo "First PID of "$appname" is "$app_pid""
while : ; do
lastpid=$(pidof "$appname")
if [[ "${app_pid}" == "${lastpid}" ]] ; then
printf "Still monitoring...\n"
else
crashtxt="Crashes will be logged in the monitoring tool directory under results.txt"
echo "FAILURE: Crash occurred at: $(date) ${crashtxt}"
date >> ./results.txt
app_pid="$lastpid"
fi
sleep 5
done
So this script will basically check the PID of the given app until you CTRL+C. If the PID of the app changes while the script is running. It will output the timestamps of when it occurred in a "results.txt" file and it will keep checking it until you press CTRL+C. Therefore I am going to use this to log all crash occurrences of my apps. Thanks a lot #Walter A

Shellscript to monitor a log file if keyword triggers then run the snmptrap command

Is there a way to monitor a log file using shell script like
tail -f /var/log/errorlog.txt then if something like down keyword appears, then generate SNMPTRAP to snmp manager and continues the monitoring
I have a SNMP script available to generate SNMPTrap and it looks like
snmptrap -v v2c -c community host "Error message"
Lets the say the script name is snmp.sh
My question is how to perform the below operation
tail the logs
if keyword[down] matches then use snmp.sh script to send alert
else leave
As per the suggestion i tried this
tail -F /data/log/test.log |
egrep -io 'got signal 15 | now exiting' |
while read -r line ;
do
case "$line" in
"got signal 15")
echo "hi"
;;
"now exiting")
echo "hi2"
;;
*)
esac
done
but the problem is tail is not working here with case statement, whenever the new log details added its not going to the case statement and echos the output
I could get the output if i use cat/less/more
Could you someone please tell what mistake i have done here ?
Thanks in advance
It sounds like the pattern you want is this:
tail -f /var/log/errorlog.txt | grep -e down -e unmounted | while read -r line
do
case "$line" in
down)
./snmp.sh …
;;
unmounted)
./snmp.sh …
;;
*)
echo "Unhandled keyword ${line}" >&2
exit 1
esac
done
try
tail -f /var/log/errorlog.txt | grep "down" | while read line; do snmp.sh; done

Unable to array values outside of function in shell script [duplicate]

Please explain to me why the very last echo statement is blank? I expect that XCODE is incremented in the while loop to a value of 1:
#!/bin/bash
OUTPUT="name1 ip ip status" # normally output of another command with multi line output
if [ -z "$OUTPUT" ]
then
echo "Status WARN: No messages from SMcli"
exit $STATE_WARNING
else
echo "$OUTPUT"|while read NAME IP1 IP2 STATUS
do
if [ "$STATUS" != "Optimal" ]
then
echo "CRIT: $NAME - $STATUS"
echo $((++XCODE))
else
echo "OK: $NAME - $STATUS"
fi
done
fi
echo $XCODE
I've tried using the following statement instead of the ++XCODE method
XCODE=`expr $XCODE + 1`
and it too won't print outside of the while statement. I think I'm missing something about variable scope here, but the ol' man page isn't showing it to me.
Because you're piping into the while loop, a sub-shell is created to run the while loop.
Now this child process has its own copy of the environment and can't pass any
variables back to its parent (as in any unix process).
Therefore you'll need to restructure so that you're not piping into the loop.
Alternatively you could run in a function, for example, and echo the value you
want returned from the sub-process.
http://tldp.org/LDP/abs/html/subshells.html#SUBSHELL
The problem is that processes put together with a pipe are executed in subshells (and therefore have their own environment). Whatever happens within the while does not affect anything outside of the pipe.
Your specific example can be solved by rewriting the pipe to
while ... do ... done <<< "$OUTPUT"
or perhaps
while ... do ... done < <(echo "$OUTPUT")
This should work as well (because echo and while are in same subshell):
#!/bin/bash
cat /tmp/randomFile | (while read line
do
LINE="$LINE $line"
done && echo $LINE )
One more option:
#!/bin/bash
cat /some/file | while read line
do
var="abc"
echo $var | xsel -i -p # redirect stdin to the X primary selection
done
var=$(xsel -o -p) # redirect back to stdout
echo $var
EDIT:
Here, xsel is a requirement (install it).
Alternatively, you can use xclip:
xclip -i -selection clipboard
instead of
xsel -i -p
I got around this when I was making my own little du:
ls -l | sed '/total/d ; s/ */\t/g' | cut -f 5 |
( SUM=0; while read SIZE; do SUM=$(($SUM+$SIZE)); done; echo "$(($SUM/1024/1024/1024))GB" )
The point is that I make a subshell with ( ) containing my SUM variable and the while, but I pipe into the whole ( ) instead of into the while itself, which avoids the gotcha.
#!/bin/bash
OUTPUT="name1 ip ip status"
+export XCODE=0;
if [ -z "$OUTPUT" ]
----
echo "CRIT: $NAME - $STATUS"
- echo $((++XCODE))
+ export XCODE=$(( $XCODE + 1 ))
else
echo $XCODE
see if those changes help
Another option is to output the results into a file from the subshell and then read it in the parent shell. something like
#!/bin/bash
EXPORTFILE=/tmp/exportfile${RANDOM}
cat /tmp/randomFile | while read line
do
LINE="$LINE $line"
echo $LINE > $EXPORTFILE
done
LINE=$(cat $EXPORTFILE)

Terminating half of a pipe on Linux does not terminate the other half

I have a filewatch program:
#!/bin/sh
# On Linux, uses inotifywait -mre close_write, and on OS X uses fswatch.
set -e
[[ "$#" -ne 1 ]] && echo "args count" && exit 2
if [[ `uname` = "Linux" ]]; then
inotifywait -mcre close_write "$1" | sed 's/,".*",//'
elif [[ `uname` = "Darwin" ]]; then
# sed on OSX/BSD wants -l for line-buffering
fswatch "$1" | sed -l 's/^[a-f0-9]\{1,\} //'
fi
echo "fswatch: $$ exiting"
And a construct i'm trying to use from a script (and I am testing with it on the command line on CentOS now):
filewatch . | while read line; do echo "file $line has changed\!\!"; done &
So what I am hoping this does is it will let me process, one line at a time, the output of inotify, which of course sends out one line for each file it has detected a change on.
Now for my script to clean stuff up properly I need to be able to kill this whole backgrounded pipeline when the script exits.
So i run it and then if I run kill on either the first part of the pipe or the second part, the other part does not terminate.
So I think if I kill the while read line part (which should be sh (zsh in the case of running on the cmd line)) then filewatch should be receiving a SIGPIPE. Okay so I am not handling that, I guess it can keep running.
If I kill filewatch, though, it looks like zsh continues with its while read line. Why?

Resources