Nested background processes and SIGINT handling - bash

Here is an example script, simple.sh, that launches a background process, sends it a signal, and verifies that the signal was handled:
#!/bin/bash
function on_signal() {
echo "1 caught $1, exiting!"
exit 0
}
function my_sleep {
trap "on_signal $1" $1
echo "1 mypid=$$"
echo "1 mybashpid=$BASHPID"
echo "1 start sleep"
for i in {1..10};
do
echo "1 sleeping $i"
sleep 1
done
echo "1 failed"
exit 1
}
signal=SIGINT
if [[ ! -z $1 ]]; then
signal=$1
fi
my_sleep $signal &
sleeppid=$!
echo ">>> sleeppid=$sleeppid"
sleep 1 # to give script time to run trap
echo ">>> sending $signal"
kill -$signal $sleeppid
for i in 0.25 0.5 1 2; do
echo ">>> trying $i"
if kill -0 $sleeppid 2> /dev/null; then
echo ">>> still running..."
sleep $i
else
echo ">> success"
exit 0
fi
done
echo "Failure"
exit 1
Now, when I run ./simple.sh, it works fine. When I run it in the background, ./simple.sh &, it works fine as well. However, when I add a wrapper, w1.sh, that runs simple.sh in the background, it suddenly stops working:
#!/bin/bash
./simple1.sh $1 &
Now ./w1.sh results in Failure. Why?
I have read https://www.cons.org/cracauer/sigint.html, and it was very informative, but I still cannot understand how the child process itself can behave differently depending on the context it was called in.
Note: I know a "workaround" to get it to behave the way I want to (w1.sh should call (./simple.sh $1) & in a subshell), but I want to understand what's going on there, and why the workaround works.
Thank you!

Related

Bash fail completely if any subshell fails

I want to run some scripts in parallel and if all succeeds I will execute some extra commands. However if any of the subshells fails I want to exit immediately.
I guess it is easier to explain with an example:
exit_in_1() {
sleep 1
echo "This should be the last thing printed"
exit 1
}
exit_in_2() {
sleep 2
echo "This should not be printed"
exit 1
}
exit_in_1 &
exit_in_2 &
echo "This should be printed immediately"
sleep 3
echo "This should not be printed also"
Above script prints all the echos.
Thanks to #Mansuro's comment I solved my problem like this:
exit_later() {
sleep $1
echo "This should be printed twice"
exit $(($1-1))
}
pids=""
for i in {1..10}; do
exit_later $i &
pids+=" $!"
done
echo "This should be printed immediately"
for p in $pids; do
if ! wait $p; then
kill $pids
exit 1
fi
done
echo "This should not be printed"
With GNU Parallel this might work:
exit_in_1() {
sleep 1
echo "This should be the last thing printed"
exit 1
}
exit_in_2() {
sleep 2
echo "This should not be printed"
exit 1
}
export -f exit_in_1 exit_in_2
parallel --halt-on-error 2 ::: 'echo "This should be printed immediately"' exit_in_1 exit_in_2 || exit 1
echo "This should not be printed also"
Or:
exit_later() {
sleep $1
echo "This should be printed twice"
exit $(($1-1))
}
export -f exit_later
echo "This should be printed immediately"
parallel -j0 --halt-on-error 2 exit_later ::: {1..10} || exit 1
echo "This should not be printed"

Simple bash timer script

Kind of suck at bash here. Looking for help. I'm trying to write a script that takes an int argument, and sleeps for 10 * argument seconds, then displays the current date and time.
I also want an infinite loop, and a message to echo when it is ctrl c'd out of.
Here's what I've got so far:
#!/bin/bash
trap "echo I'm done here && exit" INT
time=10*$1
now="$(date)"
while :
do
sleep "$time"
echo ""
echo "Current date and time: $now"
done
#!/bin/bash
trap "echo \"I'm done here\" && exit" INT
if [[ ! $1 ]]; then # check #triplee comment below
echo >&2 "Missing arg 1"
exit 1
fi
while true; do
sleep $((10 * $1))
echo "Current date and time: $(date)"
done
Check http://mywiki.wooledge.org/ArithmeticExpression
And check what #JNevill said in comments upper

How to avoid printing an error in the console in a Bash script when executing a command?

How to avoid printing an error in Bash? I want to do something like this. If the user enters a wrong argument (like a "." for example), it will just exit the program rather than displaying the error on the terminal. (I've not posted the whole code here... That's a bit long).
if [ -n "$1" ]; then
sleep_time=$1
# it doesn't work, and displays the error on the screen
sleep $sleep_time > /dev/null
if [ "$?" -eq 0 ]; then
measurement $sleep_time
else
exit
fi
# if invalid arguments passed, take the refreshing interval from the user
else
echo "Proper Usage: $0 refresh_interval(in seconds)"
read -p "Please Provide the Update Time: " sleep_time
sleep $sleep_time > /dev/null
if [ "$?" -eq 0 ]; then
measurement $sleep_time
else
exit
fi
fi
2>/dev/null will discard any errors. Your code can be simplified like this:
#!/usr/bin/env bash
if [[ $# -eq 0 ]]; then
echo "Usage: $0 refresh_interval (in seconds)"
read -p "Please provide time: " sleep_time
else
sleep_time=$1
fi
sleep "$sleep_time" 2>/dev/null || { echo "Wrong time" >&2; exit 1; }
# everything OK - do stuff here
# ...

Bash trap - exit only at the end of loop

I´ve got a bash script which has different cases - each with a for or a while loop. I´ve declared a trap function to get the chance to exit the script and the loop. But if I do this the script will exit immediately - I want to exit the loop at the end of the loop run because each loop run takes a long time.
Here is a short version of my script:
CleanUp() {
echo "Trap exit detected"
rm -f $TMPFILE1
rm -f $TMPFILE2
StopPreventSleep
echo "... and ready!" && exit
}
trap CleanUp EXIT INT TERM SIGINT SIGTERM SIGTSTP
case $1 in
check)
for FILES in "${SRCFILES[#]}"
do
[somemagic]
done
;;
read)
for FILES in "${SRCFILES[#]}"
do
[somemagic]
done
;;
write)
while [ -n "$line" ]
do
[somemagic]
done
;;
I want that the script only could exit after doing [somemagic] in each loop (depends on the parameter $1 = which case is choosen).
change the line
echo "... and ready!" && exit
to:
QUIT=1
And after each of your [somemagic], add the some extra logic as below:
...
[somemagic]
if [ ! -z $QUIT ]; then
exit
fi

Shell Script: Exit call not working on using back quotes

I am using exit 1 to stop a shell script execution when error occured.
Shell Script
test() {
mod=$(($1 % 10))
if [ "$mod" = "0" ]
then
echo "$i";
exit 1;
fi
}
for i in `seq 100`
do
val=`test "$i"`
echo "$val"
done
echo "It's still running"
Why it's not working?. How can I stop the shell script execution?
The shell that exit is exiting is the one started by the command substitution, not the shell that starts the command substitution.
Try this:
for i in `seq 100`
do
val=`test "$i"` || exit
echo "$val"
done
echo "It's still running"
You need to explicitly check the exit code of the command substitution (which is passed through by the variable assignment) and call exit again if it is non-zero.
Incidentally, you probably want to use return in a function rather than exit. Let the function caller decide what to do, unless the error is so severe that there is no logical alternative to exiting the shell:
test () {
if (( $1 % 10 == 0 )); then
echo "$i"
return 1
fi
}
The exit command terminates only the (sub)shell in which it is executed.
If you want to terminate the entire script, you have to check the exit status
($?) of the function and react accordingly:
#!/bin/bash
test() {
mod=$(($1 % 10))
if [ "$mod" -eq "0" ]
then
echo "$i";
exit 1;
fi
}
for i in `seq 100`
do
val=`test "$i"`
if [[ $? -eq 1 ]]
then
exit 1;
fi
echo "$val"
done
echo "It's still running"

Resources