Timeout for grep in bash script - bash

I need to grep for a specific string in a specific time on an input:
trap "kill 0" EXIT SIGINT SIGTERM
RESULT=$(adb logcat MyTag:V *:S | grep -m 1 "Hello World") &
sleep 10
if [ "$RESULT" = "" ]; then
echo "Timeout!"
else
echo "found"
fi
with the trap the subshell gets killed correctly but i see that the grep does not work anymore now. adb logcat is the only process running in the subshell, when executing the script

You could open a file descriptor as input with process substitution. After n seconds you could read the result.
{
sleep 10
IFS= read -rd '' -u 4 RESULT
if [ "$RESULT" = "" ]; then
echo "Timeout!"
else
echo "found"
fi
} 4< <(adb logcat MyTag:V *:S | grep -m 1 "Hello World")
You could also use exec to keep it not just within {}.
Although I still wonder why you have to place the command in the background and use sleep to wait for it. It runs with a subshell so the value saved in RESULT would always be lost.
RESULT=$(adb logcat MyTag:V *:S | grep -m 1 "Hello World") &
sleep 10
RESULT=$(adb logcat MyTag:V *:S | grep -m 1 "Hello World")

Can you kill the subshell using $! (the pid of the last process invoked)

Related

shell script stops proceeding inside the loop

Using bash version 5, I have a loop that goes through a list, and uses each item in that list in a find command inside the loop, and then append that find result to an empty array.
This code however stops at saving 1st result successfully in pid and script exits. I have tried trap and set -x with no progress.
Anyone understands what's happening here ?
#!/bin/bash
set -e
set -o pipefail
set -vx
## run cleanup on signal 1, 2, 3, 6
trap cleanup 1 2 3 6
cleanup()
{
echo "Caught Signal $? ... cleaning up."
}
r=$(cat /proc/net/tcp |grep " 01 " |awk '{print $10}')
inodes=()
inodes+=($r)
pattern="^.*(proc)\/\K([a-zA-Z0-9_-]*)(?=\/[a-zA-Z0-9]*)?"
con_pids=()
for inode in ${inodes[*]}
do
echo inode number is: $inode
pid=$(find /proc/* -type l -ls 2>/dev/null |grep "socket:\[$inode\]" | grep -m1 -oP $pattern)
echo "code didn't stop"
# echo $?
con_pids+=($pid)
#con_pids=(${con_pids[*]} "$pid")
done
echo ${con_pids[*]}

wait doesn't wait for the processes in the while loop to finish

Here is my code:
count=0
head -n 10 urls.txt | while read LINE; do
curl -o /dev/null -s "$LINE" -w "%{time_total}\n" &
count=$((count+1))
[ 0 -eq $((count % 3)) ] && wait && echo "process wait" # wait for 3 urls
done
echo "before wait"
wait
echo "after wait"
I am expecting the last curl to finish before printing the last echo, but actually it's not the case:
0.595499
0.602349
0.618237
process wait
0.084970
0.084243
0.099969
process wait
0.067999
0.068253
0.081602
process wait
before wait
after wait
➜ Downloads 0.088755 # already exited the script
Does anyone know why it's happening? And how to fix this?
As described in BashFAQ #24, this is caused by your pipeline causing the while loop to be performed in a different shell from the rest of your script.
Consequently, your curls are subprocesses of that subshell, not the outer interpreter; so the outer interpreter cannot wait for them.
This can be resolved by not piping to while read, but instead redirecting its input in a way that doesn't shuffle it into a pipeline element -- as with <(...), a process substitution:
#!/usr/bin/env bash
# ^^^^ - NOT /bin/sh; also, must not start with "sh scriptname"
count=0
while IFS= read -r line; do
curl -o /dev/null -s "$line" -w "%{time_total}\n" &
count=$((count+1))
(( count % 3 == 0 )) && { wait; echo "process wait"; } # wait for 3 urls
done < <(head -n 10 urls.txt)
echo "before wait"
wait
echo "after wait"
why it's happening?
Because you run the processes in the subshell, the parent process can't wait for them.
$ echo | { echo subshell; sleep 100 & }
$ wait # exits immiedately
$
Call wait from the same process the background processes were spawned:
someotherthing | {
while someotherthing; do
something &
done
wait # will wait for something
}
And how to fix this?
I recommend not to use a crude while read loop and use different approach using some tool. Use GNU xargs with -P option to run 3 processes concurently:
head -n 10 urls.txt | xargs -P3 -n1 -d '\n' curl -o /dev/null -w "%{time_total}\n" -s
But you could just use move wait into the subshell as above, or make the while loop to be executed in the parent shell alternatively.

bash sh script nohup excuting not complete?

i have a problem, plese watch this code. (j_restart.sh file)
#!/bin/bash
printf "Killing j-Chat server script... "
nyret=`pkill -f index.php`
printf "OK !\n"
printf "Wait killing instances."
while : ; do
nyret=`netstat -ap | grep :8008 | wc -l`
if [ "$nyret" == "0" ]; then
printf "OK !\n"
break
fi
printf "."
sleep 3
done
echo "Runing j-Chat server script... "
nyret=`nohup php -q /home/jChat/public_html/index.php < /dev/null &`
echo "OK !"
echo "j-Chat Server Working ON !";
ssh return val :
root#server [~]# sh /home/jChat/public_html/j_restart.sh
Killing jChat Server Script... OK !
Wait killing instances................ OK !
Runing jChat Server Script...
nohup: redirecting stderr to stdout
(and waiting not jump next line..)
im press manualy ctrl+c keys
^C
root#server [~]#
How to fix this problem ? why not working complete ? Stop and wait line 16...how to countinue next line 17 and 18... ?? Help me please..
Here's a simpler example reproducing your problem:
nyret=`nohup sleep 30 < /dev/null &`
echo "This doesn't run (until sleep exits)"
The problem is the shell is waiting to capture all output from your command. It runs in the background, but it still keeps the pipe open, so the shell waits.
The solution is to not capture the output, because you don't use it anyways:
nohup sleep 30 < /dev/null &
echo "This runs fine"

Determining if process is running using pgrep

I have a script that I only want to be running one time. If the script gets called a second time I'm having it check to see if a lockfile exists. If the lockfile exists then I want to see if the process is actually running.
I've been messing around with pgrep but am not getting the expected results:
#!/bin/bash
COUNT=$(pgrep $(basename $0) | wc -l)
PSTREE=$(pgrep $(basename $0) ; pstree -p $$)
echo "###"
echo $COUNT
echo $PSTREE
echo "###"
echo "$(basename $0) :" `pgrep -d, $(basename $0)`
echo sleeping.....
sleep 10
The results I'm getting are:
$ ./test.sh
###
2
2581 2587 test.sh(2581)---test.sh(2587)---pstree(2591)
###
test.sh : 2581
sleeping.....
I don't understand why I'm getting a "2" when only one process is actually running.
Any ideas? I'm sure it's the way I'm calling it. I've tried a number of different combinations and can't quite seem to figure it out.
SOLUTION:
What I ended up doing was doing this (portion of my script):
function check_lockfile {
# Check for previous lockfiles
if [ -e $LOCKFILE ]
then
echo "Lockfile $LOCKFILE already exists. Checking to see if process is actually running...." >> $LOGFILE 2>&1
# is it running?
if [ $(ps -elf | grep $(cat $LOCKFILE) | grep $(basename $0) | wc -l) -gt 0 ]
then
abort "ERROR! - Process is already running at PID: $(cat $LOCKFILE). Exitting..."
else
echo "Process is not running. Removing $LOCKFILE" >> $LOGFILE 2>&1
rm -f $LOCKFILE
fi
else
echo "Lockfile $LOCKFILE does not exist." >> $LOGFILE 2>&1
fi
}
function create_lockfile {
# Check for previous lockfile
check_lockfile
#Create lockfile with the contents of the PID
echo "Creating lockfile with PID:" $$ >> $LOGFILE 2>&1
echo -n $$ > $LOCKFILE
echo "" >> $LOGFILE 2>&1
}
# Acquire lock file
create_lockfile >> $LOGFILE 2>&1 \
|| echo "ERROR! - Failed to acquire lock!"
The argument for pgrep is an extended regular expression pattern.
In you case the command pgrep $(basename $0) will evaluate to pgrep test.sh which will match match any process that has test followed by any character and lastly followed by sh. So it wil match btest8sh, atest_shell etc.
You should create a lock file. If the lock file exists program should exit.
lock=$(basename $0).lock
if [ -e $lock ]
then
echo Process is already running with PID=`cat $lock`
exit
else
echo $$ > $lock
fi
You are already opening a lock file. Use it to make your life easier.
Write the process id to the lock file. When you see the lock file exists, read it to see what process id it is supposedly locking, and check to see if that process is still running.
Then in version 2, you can also write program name, program arguments, program start time, etc. to guard against the case where a new process starts with the same process id.
Put this near the top of your script...
pid=$$
script=$(basename $0)
guard="/tmp/$script-$(id -nu).pid"
if test -f $guard ; then
echo >&2 "ERROR: Script already runs... own PID=$pid"
ps auxw | grep $script | grep -v grep >&2
exit 1
fi
trap "rm -f $guard" EXIT
echo $pid >$guard
And yes, there IS a small window for a race condition between the test and echo commands, which can be fixed by appending to the guard file, and then checking that the first line is indeed our own PID. Also, the diagnostic output in the if can be commented out in a production version.

Little Bash Script: Catch Errors?

I've written (well, remixed to arrive at) this Bash script
# pkill.sh
trap onexit 1 2 3 15 ERR
function onexit() {
local exit_status=${1:-$?}
echo Problem killing $kill_this
exit $exit_status
}
export kill_this=$1
for X in `ps acx | grep -i $1 | awk {'print $1'}`; do
kill $X;
done
it works fine but any errors are shown to the display. I only want the echo Problem killing... to show in case of error. How can I "catch" (hide) the error when executing the kill statement?
Disclaimer: Sorry for the long example, but when I make them shorter I inevitably have to explain "what I'm trying to do."
# pkill.sh
trap onexit 1 2 3 15 ERR
function onexit() {
local exit_status=${1:-$?}
echo Problem killing $kill_this
exit $exit_status
}
export kill_this=$1
for X in `ps acx | grep -i $1 | awk {'print $1'}`; do
kill $X 2>/dev/null
if [ $? -ne 0 ]
then
onexit $?
fi
done
You can redirect stderr and stdout to /dev/null via something like pkill.sh > /dev/null 2>&1. If you only want to suppress the output from the kill command, only apply it to that line, e.g., kill $X > /dev/null 2>&1;
What this does is take send the standard output (stdout) from kill $X to /dev/null (that's the > /dev/null), and additionally send stderr (the 2) into stdout (the 1).
For my own notes, here's my new code using Paul Creasey's answer:
# pkill.sh: this is dangerous and should not be run as root!
trap onexit 1 2 3 15 ERR
#--- onexit() -----------------------------------------------------
# #param $1 integer (optional) Exit status. If not set, use `$?'
function onexit() {
local exit_status=${1:-$?}
echo Problem killing $kill_this
exit $exit_status
}
export kill_this=$1
for X in `ps acx | grep -i "$1" | awk {'print $1'}`; do
kill $X 2>/dev/null
done
Thanks all!

Resources