Identifying received signal name in Bash
(If you only have the number of a signal and want the name, kill -l $SIGNAL_NUM
prints the name of a signal; you can avoid that by using the signal names instead of numbers in your call to trap
as below.)
This answer says that there's no way to access the signal name, but if you have a separate function for each signal that you trap, then you already know the signal name:
trap 'echo trapped the HUP signal' HUP
trap 'echo different trap for the INT signal' INT
In many cases, that may be sufficient, but another answer on that same question uses that fact to provide a workaround to fake the behavior you want. It takes a function and a list of signals and sets a separate trap for each signal on that function called with the signal name, so internally it's actually a separate function for each signal but it looks like a single trap on a single function that gets the signal name as an argument:
Code:
#!/bin/bash
trap_with_arg() {
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
done
}
func_trap() {
echo "Trapped: $1"
}
trap_with_arg func_trap INT TERM EXIT
echo "Send signals to PID $$ and type [enter] when done."
read # Wait so the script doesn't exit.
If I run that, then I can send signals to the process and I get output like
Trapped: INT
Trapped: TERM
Trapped: EXIT
Is it possible to detect *which* trap signal in bash?
No documentation hints of any argument or variable holding the signal that was trapped, so you'll have to write a function/trap statement for each trap you want to behave differently.
Find signal number by signal name
Try constraining the match with a word boundary:
trap -l | sed -nr 's/.*\b([0-9]+)\) SIGTSTP.*/\1/p'
This tested correctly [for me] for the target string as well as SIGPIPE.
Identify whether a process was killed by a signal in bash
wait
is a syscall and also a bash builtin.
To differentiate the two cases from bash run the program in the background and use the builtin wait
to report the outcome.
Following are examples of both a non-zero exit code and an uncaught signal. These examples use the exit
and kill
bash builtins in a child bash shell, instead of a child bash shell you would run your program.
$ bash -c 'kill -s SIGTERM $$' & wait
[1] 36068
[1]+ Terminated: 15 bash -c 'kill -s SIGTERM $$'
$ bash -c 'exit 143' & wait
[1] 36079
[1]+ Exit 143 bash -c 'exit 143'
$
As to why you see Terminated
printed to the terminal even when you redirect stdout and stderr the reason is that is printed by bash, not by the program.
Update:
By explicitly using the wait
builtin you can now redirect its stderr (with the exit status of the program) to a separate file.
The following examples show the three types of termination: normal exit 0, non-zero exit, and uncaught signal. The results reported by wait
are stored in files tagged with the PID of the corresponding program.
$ bash -c 'exit 0' & wait 2> exit_status_pid_$!
[1] 40279
$ bash -c 'exit 143' & wait 2> exit_status_pid_$!
[1] 40291
$ bash -c 'kill -s SIGTERM $$' & wait 2> exit_status_pid_$!
[1] 40303
$ for f in exit_status_pid*; do echo $f: $(cat $f); done
exit_status_pid_40279: [1]+ Done bash -c 'exit 0'
exit_status_pid_40291: [1]+ Exit 143 bash -c 'exit 143'
exit_status_pid_40303: [1]+ Terminated: 15 bash -c 'kill -s SIGTERM $$'
$
In bash script, what happens when a signal is trapped
$ cat foo.sh
_sigint()
{
echo "received SIGINT"
}
trap _sigint INT
sleep 10000
echo "status=$?"
$ bash foo.sh # <-- start foo.sh and then press CTRL-C
^Creceived SIGINT
status=130
Some explanation:
sleep
runs as a child process ofbash
.bash
andsleep
are running in the same process group which is now the foreground process group.When CTRT-C is pressed, it'll generate
SIGINT
and the signal will be sent to all processes in the foreground process group so bothbash
andsleep
will receiveSIGINT
.According to bash manual (can also see
man bash
)If bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes.
So here
bash
would wait forsleep
to be killed (The default SIGINT behavior is to terminate the process. See signal(7)) first and thenbash
runs itsSIGINT
trap.According to bash manaul
The return value of a simple command is its exit status, or
128+n
if the command is terminated by signaln
.The
SIGINT
is 2 (seekill -l
) sosleep
's exit status is128+2=130
.
How can I tell in Linux which process sent my process a signal
Two Linux-specific methods are SA_SIGINFO
and signalfd()
, which allows programs to receive very detailed information about signals sent, including the sender's PID.
Call
sigaction()
and pass to it astruct sigaction
which has the desired signal handler insa_sigaction
and theSA_SIGINFO
flag insa_flags
set. With this flag, your signal handler will receive three arguments, one of which is asiginfo_t
structure containing the sender's PID and UID.Call
signalfd()
and readsignalfd_siginfo
structures from it (usually in some kind of a select/poll loop). The contents will be similar tosiginfo_t
.
Which one to use depends on how your application is written; they probably won't work well outside plain C, and I wouldn't have any hope of getting them work in Java. They are also unportable outside Linux. They also likely are the Very Wrong Way of doing what you are trying to achieve.
Bash script catch signal but wait afterwards for processes to terminate
Something rewritted
In order to avoid some useless forks.
clock(){ local prefix=C interval=2
trap : RTMIN{,+{{,1}{1,2,3,4,5},6,7,8,9,10}}
while :;do
printf "%s: %(%d.%m %H:%M:%S)T\n" $prefix -1
sleep $interval
done
}
volume(){ local prefix=V vol=() field playback val foo
while IFS=':[]' read field playback val foo;do
[ "$playback" ] && [ -z "${playback//*Playback*}" ] && [ "$val" ] &&
vol+=(${val%\%})
done < <(amixer get Master)
suffix='%%'
if [ "$vol" = "off" ] ;then
icon="&" #alternative: deaf: mute:
suffix=''
elif (( vol > 50 )) ;then icon="("
elif (( vol > 30 )) ;then icon="("
else icon="'"
fi
printf -v values "%3s$suffix " ${vol[@]}
printf "%s%s %s\n" $prefix "$icon" "$values"
}
clock & volume &
trap volume RTMIN+2
trap : RTMIN{,+{{,1}{1,3,4,5},6,7,8,9,10,12}}
echo -e "To get status, run:\n kill -RTMIN+2 $$"
while :;do wait ;done
Regarding my last comment about stereo bug, there is a volume function working for stereo, mono or even quadra:
volume(){
local prefix=V vol=() field playback val foo
local -i overallvol=0
while IFS=':[]' read field playback val foo ;do
[ "$playback" ] && [ -z "${playback//*Playback*}" ] && [ "$val" ] && {
vol+=($val)
val=${val%\%}
overallvol+=${val//off/0}
}
done < <(
amixer get Master
)
overallvol=$overallvol/${#vol[@]}
if (( overallvol == 0 )) ;then
icon="&"
elif (( overallvol > 50 )) ;then
icon="("
elif (( overallvol > 30 )) ;then
icon="("
else
icon="'"
fi
printf "%s%s %s\n" $prefix "$icon" "${vol[*]}"
}
or even:
volume(){
local prefix=V vol=() field playback val foo icons=(⏻ ¼ ¼ ¼ ½ ½ ¾ ¾ ¾ ¾ ¾)
local -i overallvol=0
while IFS=':[]' read field playback val foo ;do
[ "$playback" ] && [ -z "${playback//*Playback*}" ] && [ "$val" ] && {
vol+=($val)
val=${val%\%}
overallvol+=${val//off/0}
}
done < <(
amixer get Master
)
overallvol=$overallvol/${#vol[@]}
printf "%s%s %s\n" $prefix "${icons[(9+overall)/10]}" "${vol[*]}"
Some explanations
Regarding useless forks in volume()
function
I've posted there some ideas to improve the job, reducing resource eating and doing same job of choosing an icon as function of current volume set.
About while :;do wait;done
loop
As requested sample stand for an infinite loop in backgrounded sub function, the main script use same infinite loop.
But as question title stand for wait afterwards for processes to terminate, I have to agree with oguz-ismail's comment.
In fact, last line would better be written:
until wait;do :;done
For more information on how wait
command work and good practice, please have a look on good oguz-ismail's answer
What is the bash command on Ubuntu to list all the signals?
You can use the trap
builtin:
trap -l
From help trap
:
-l
print a list of signal names and their corresponding numbers
Related Topics
How to Exclude Absolute Paths for Tar
Replacing Environment Variables in a Properties File
Differencebetween .So and .A Files
Apt-Get Error: Sub-Process /Usr/Bin/Dpkg Returned an Error Code (1)
How to Check Status of Urls from Text File Using Bash Shell Script
Search for a Cronjob with Crontab -L
Best Approach to Writing a Generic Installer for a Linux Application
How to Tell What a Linux Process Is Waiting For
Hook Functions for Linux Filesystem
Identify Other End of a Unix Domain Socket Connection
Check If Rsync Command Ran Successful
Bash Alias with Argument and Autocompletion