Bash not trapping interrupts during rsync/subshell exec statements
How about just having all the output from point X be redirected to tee without having to repeat it everywhere and mess with all the sub-shells and execs ... (hope I didn't miss something)
#!/bin/bash
logfile=/path/to/file;
directory1=/path/to/dir
directory2=/path/to/dir
exec > >(exec tee -a $logfile) 2>&1
cleanup () {
echo "Cleaning up!"
#do stuff
trap - EXIT
}
trap cleanup EXIT
sleep 10
rsync --progress -av --delete $directory1 /var/tmp/$directory2
Parent trap visible but not run by subshell
You'll notice that the traps trigger exactly according to spec. It's just the output from trap
that's unexpected.
This is a feature in Bash 4.2 (release notes):
b. Subshells begun to execute command substitutions or run shell functions or
builtins in subshells do not reset trap strings until a new trap is
specified. This allows $(trap) to display the caller's traps and the
trap strings to persist until a new trap is set.
Normally, people would take this for granted. Consider this totally unsurprising Bash exchange:
bash$ trap
trap -- 'foo' EXIT
trap -- 'bar' SIGINT
bash$ trap | grep EXIT
trap -- 'foo' EXIT
Now look at the result in other shells like Dash, Ksh or Zsh:
dash$ trap
trap -- 'foo' EXIT
trap -- 'bar' INT
dash$ trap | grep EXIT
(no output)
This is perhaps more correct, but I doubt many people would expect it.
rsync suppress SIGINT for trap outside
Why does it first return code 20 didn't make the loop break?
You are correct that rsync
does catch certain signals and exit with RERR_SIGNAL
(20).
How to let the trap catch the SIGINT signal but rsync, if not, what should I do?
Since rsync
has its own handlers, you can't do anything ( could use some hacks to override signal handlers within rsync
with LD_PRELOAD
for example. But it may be unnecessarily complicated). Since your traps are in the current shell, you wouldn't know whether the "command" was signaled or exit with non-zero.
I'd assume you want to your retry
to be generic and you don't want special handling of rsync
(e.g., a different command may exit with 75 on signals and you don't want to dealing with special cases).
The problem is your trap handlers isn't active as the the signal is received by the current process running process (rsync
). You could instead run your command in the background and wait for it to complete. This would allow your catch signals from retry
. On receiving a signal, it simply kills the child process.
#!/bin/bash
retry()
{
declare -i number=$1
declare -i i
declare -i pid
declare -i interrupted=0
trap "echo Exiting...; interrupted=1" SIGINT SIGTERM SIGQUIT
shift
# Turn off "monitor mode" so the shell doesn't report terminating background jobs.
set +m
for ((i = 0; i < number; ++i)); do
echo "\n-- Retry ${i}th time(s) --\n"
$@ &
pid=$!
# If command succeeded, break
wait $pid && break
# If we receive one of the signals, break
[[ $interrupted == 1 ]] && kill $pid && break
done
# Switch back to default behaviour
set -m
trap - SIGINT SIGTERM SIGQUIT
}
Note that SIGKILL
can't be caught. So there's no point in setting a trap for it. So I have removed it.
Exiting a function with trap and its error code
You must use exit
instead of return
. So this would be
trap 'printf "\nAborting\n"; exit 1' SIGINT
bash script exits with zero status even after kill signal
I assume the missing quote in echo "Script Status: $1 | tee -a ${LOG}
is not relevant to the question.
When you want a function to handle the trap, you need to export that function.
And when you want to kill children, you should add these in your trap-function.
I tested these adjustments with a sleep
command, it should work for rsync
too.
#!/bin/bash
exitscript() {
echo "Script Status: $1"
(( $pleasekill > 0 )) && kill ${pleasekill}
echo "> End Date: $(date +'%d.%m.%Y %H:%M:%S')"
exit $1
}
# Export the function exitscript
export exitscript
######START#######
pleasekill=0
trap "exitscript 1" 1 2 23 24 25
# Start I/O-friendly rsync function
sleep 30 &
pleasekill=$!
wait
exitscript 2
When you test this with the first script, use ^C
or kill -1 pid_of_backup
.
EXIT trap (killing background loop) not also killing processes run by that loop
Not as simple...
Problem is: when using ( ... )
syntax, you will create a subshell... Let's show:
In some terminal console:
tty
/dev/pts/24
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
29322 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
( echo foo; sleep 300 ) &
[1] 30214
foo
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
30214 pts/24 S 0:00 \_ bash
30215 pts/24 S 0:00 | \_ sleep 300
30377 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
ps $!
PID TTY STAT TIME COMMAND
30214 pts/24 S 0:00 bash
So $!
is bash
subshell, and sleep
has another pid.
So killing $!
will only kill bash:
kill $!
[1]+ Terminated ( echo foo; sleep 300 )
but
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
30793 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
30215 pts/24 S 0:00 sleep 300
sleep is still there.
Solution #1:
Use exec
on last long-running command:
( echo foo; exec sleep 300 ) &
[1] 31557
foo
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
31557 pts/24 S 0:00 \_ sleep 300
31558 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
then
kill $!
[1]+ Terminated ( echo foo; exec sleep 300 )
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
31706 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
Seem ok.
For loop, things are more, hem...
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
1176 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
for ((i=3;i--;)){ sleep 300 & trap "kill $!;exit" 0 1 2 3 6 9 15;wait $!;} &
[1] 1178
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
1178 pts/24 S 0:00 \_ bash
1179 pts/24 S 0:00 | \_ sleep 300
1180 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
Well, now:
kill -1 $!
[1]+ Done for ((i=3; i--; 1))
do
sleep 300 & trap "kill $!;exit" 0 1 2 3 6 9 15; wait $!;
done
ps --tty pts/24 fw
PID TTY STAT TIME COMMAND
28983 pts/24 Ss 0:00 bash
1299 pts/24 R+ 0:00 \_ ps --tty pts/24 fw
Seem fine!
Related Topics
Sending Keycode to Xorg + Wine with Bash Script
How to Find the Particular Text Stored in the File "Data.Txt" and It Occurs Only Once
How Does One Determine the Page Frame Number for Device Memory
Does Using Xvfb to Run Opengl Effects Version
How to List Recently Deleted Files from a Directory
Cmake Set Environment Variable
Block Device Information Without Mounting in Linux
Python Read Linux Memory Process Error (/Proc/$Pid/Mem)
I.Mx35 Suspend CPU and Ddr2 from Iram
How to Add an User and Re Set the Root User in Yocto
Sed Error:Bad Option in Substitution Expression
Difference Between --Cap-Add=Net_Admin and Add Capabilities in .Yml
Scp: How to Find Out That Copying Was Finished
How to Pass Command Line Parameters with Quotes Stored in Single Variable
Unterminated Address Regex While Using Sed