SIGINT in bash script - linux

I have the following bash script.
#!/bin/bash
while :
do
sleep 2
infiniteProgramm -someParametrs
sleep 10
#in this line I need to stop my infiniteProgramm with bash command (SIGINT) (like Ctrl+C, but automatic)
clear
done
How can I send a SIGINT signal to my infiniteProgramm?

First: run infiniteProgram in the background:
infiniteProgram -someParameters &
Second: retrieve its PID from $!.
pid=$!
Third: kill it.
sleep 10
kill -2 $pid
2 corresponds to SIGINT, see kill -l for the list of all the signals.

Related

what does pkill -KILL -f java do?

I am trying to understand what exactly each line of this code does:
cat << EOF > /tmp/kill_loop.sh
#!/bin/bash
while true;
do
pkill -KILL java
pkill -KILL python
sleep 1
done
EOF
nohup /bin/bash /tmp/kill_loop.sh &
but most importantly what pkill -KILL -f does
From the man page for pkill:
The pkill command searches the process table on the running system and
signals all processes that match the criteria given on the command line.
The -KILL argument is specifying which Linux process signal to send:
Signal name Signal value Effect
SIGHUP 1 Hangup
SIGINT 2 Interrupt from keyboard
SIGQUIT 3 Quit
SIGABRT 6 Abort
SIGKILL 9 Kill signal
SIGTERM 15 Termination signal - allow an orderly shutdown
SIGSTOP 17,19,23 Stop the process
See https://linux.die.net/man/1/pkill and https://linux.die.net/man/7/signal for more info.
Edit: In your example, java is being passed in as the pattern operand:
pattern -
Specifies an Extended Regular Expression for matching against the process names or command lines.

Why trap does not work to interrupt nohup command?

this is a script, the name is test.sh
#!/bin/bash
cleanup(){
echo "Caught SIGINT ..."
exit 1
}
trap 'cleanup' 3
while :;do
echo "a"
sleep 3
done
if I run nohup ./test.sh & in linux bash directly , it can catch kill -3 PID signal ,
but if I write nohup ./test.sh & in a script file that name is go.sh) ,and I run go.sh , an then test.sh can't catch kill -3 PID signal
how can I write the go.sh script to make sure test.sh catch -3 signal ?
kill -3 (SIGQUIT) would not work. According to bash's manual:
When bash is interactive, in the absence of any traps, it ignores SIGTERM (so that kill 0 does not kill an interactive shell), and SIGINT is caught and handled (so that the wait builtin is interruptible). In all cases, bash ignores SIGQUIT. If job control is in effect, bash ignores SIGTTIN, SIGTTOU, and SIGTSTP.

Don't show the output of kill command in a Linux bash script [duplicate]

How can you suppress the Terminated message that comes up after you kill a
process in a bash script?
I tried set +bm, but that doesn't work.
I know another solution involves calling exec 2> /dev/null, but is that
reliable? How do I reset it back so that I can continue to see stderr?
In order to silence the message, you must be redirecting stderr at the time the message is generated. Because the kill command sends a signal and doesn't wait for the target process to respond, redirecting stderr of the kill command does you no good. The bash builtin wait was made specifically for this purpose.
Here is very simple example that kills the most recent background command. (Learn more about $! here.)
kill $!
wait $! 2>/dev/null
Because both kill and wait accept multiple pids, you can also do batch kills. Here is an example that kills all background processes (of the current process/script of course).
kill $(jobs -rp)
wait $(jobs -rp) 2>/dev/null
I was led here from bash: silently kill background function process.
The short answer is that you can't. Bash always prints the status of foreground jobs. The monitoring flag only applies for background jobs, and only for interactive shells, not scripts.
see notify_of_job_status() in jobs.c.
As you say, you can redirect so standard error is pointing to /dev/null but then you miss any other error messages. You can make it temporary by doing the redirection in a subshell which runs the script. This leaves the original environment alone.
(script 2> /dev/null)
which will lose all error messages, but just from that script, not from anything else run in that shell.
You can save and restore standard error, by redirecting a new filedescriptor to point there:
exec 3>&2 # 3 is now a copy of 2
exec 2> /dev/null # 2 now points to /dev/null
script # run script with redirected stderr
exec 2>&3 # restore stderr to saved
exec 3>&- # close saved version
But I wouldn't recommend this -- the only upside from the first one is that it saves a sub-shell invocation, while being more complicated and, possibly even altering the behavior of the script, if the script alters file descriptors.
EDIT:
For more appropriate answer check answer given by Mark Edgar
Solution: use SIGINT (works only in non-interactive shells)
Demo:
cat > silent.sh <<"EOF"
sleep 100 &
kill -INT $!
sleep 1
EOF
sh silent.sh
http://thread.gmane.org/gmane.comp.shells.bash.bugs/15798
Maybe detach the process from the current shell process by calling disown?
The Terminated is logged by the default signal handler of bash 3.x and 4.x. Just trap the TERM signal at the very first of child process:
#!/bin/sh
## assume script name is test.sh
foo() {
trap 'exit 0' TERM ## here is the key
while true; do sleep 1; done
}
echo before child
ps aux | grep 'test\.s[h]\|slee[p]'
foo &
pid=$!
sleep 1 # wait trap is done
echo before kill
ps aux | grep 'test\.s[h]\|slee[p]'
kill $pid ## no need to redirect stdin/stderr
sleep 1 # wait kill is done
echo after kill
ps aux | grep 'test\.s[h]\|slee[p]'
Is this what we are all looking for?
Not wanted:
$ sleep 3 &
[1] 234
<pressing enter a few times....>
$
$
[1]+ Done sleep 3
$
Wanted:
$ (set +m; sleep 3 &)
<again, pressing enter several times....>
$
$
$
$
$
As you can see, no job end message. Works for me in bash scripts as well, also for killed background processes.
'set +m' disables job control (see 'help set') for the current shell. So if you enter your command in a subshell (as done here in brackets) you will not influence the job control settings of the current shell. Only disadvantage is that you need to get the pid of your background process back to the current shell if you want to check whether it has terminated, or evaluate the return code.
This also works for killall (for those who prefer it):
killall -s SIGINT (yourprogram)
suppresses the message... I was running mpg123 in background mode.
It could only silently be killed by sending a ctrl-c (SIGINT) instead of a SIGTERM (default).
disown did exactly the right thing for me -- the exec 3>&2 is risky for a lot of reasons -- set +bm didn't seem to work inside a script, only at the command prompt
Had success with adding 'jobs 2>&1 >/dev/null' to the script, not certain if it will help anyone else's script, but here is a sample.
while true; do echo $RANDOM; done | while read line
do
echo Random is $line the last jobid is $(jobs -lp)
jobs 2>&1 >/dev/null
sleep 3
done
Another way to disable job notifications is to place your command to be backgrounded in a sh -c 'cmd &' construct.
#!/bin/bash
# ...
pid="`sh -c 'sleep 30 & echo ${!}' | head -1`"
kill "$pid"
# ...
# or put several cmds in sh -c '...' construct
sh -c '
sleep 30 &
pid="${!}"
sleep 5
kill "${pid}"
'
I found that putting the kill command in a function and then backgrounding the function suppresses the termination output
function killCmd() {
kill $1
}
killCmd $somePID &
Simple:
{ kill $! } 2>/dev/null
Advantage? can use any signal
ex:
{ kill -9 $PID } 2>/dev/null

Spawn parallel processes in foreground

using bash in linux is it possible to spawn parallel processes in the foreground? For example the following :
top.sh
#!/bin/bash
./myscript1.sh &
./myscript2.sh &
will spawn two processes in parallel as background threads. However is it possible to spawn these as foreground threads? The aim is to automatically kill myscript1.sh and myscript2.sh, when top.sh is killed. Thanks
You can only have one job in the foreground. You can make your script react to any signal that reaches it and forward the signal to other jobs. You need to make sure your script sticks around, if you want to have a central way of killing the subprocesses: call wait so that your script will not exit until all the jobs have died or the script itself is killed.
#!/bin/bash
jobs=
trap 'kill -HUP $jobs' INT TERM HUP
myscript1.sh & jobs="$jobs $!"
myscript2.sh & jobs="$jobs $!"
wait
You can still kill only the wrapper script by sending it a signal that it doesn't catch, such as SIGQUIT (which I purposefully left out) or SIGKILL (which can't be caught).
There's a way to have all the processes in the foreground: connect them through pipes. Ignore SIGPIPE so that the death of a process doesn't kill the previous one. Save and restore stdin and stdout through other file descriptors if you need them. This way the script and the background tasks will be in the same process group, so pressing Ctrl+C will kill both the wrapper script and the subprocesses. If you kill the wrapper script directly, that won't affect the subprocesses; you can kill the process group instead by passing the negative of the PID of the wrapper script (e.g. kill -TERM -1234).
trap '' PIPE
{
myscript1.sh <&3 >&4 |
myscript2.sh <&3 >&4
} 3<&0 4>&1
Using GNU Parallel your script would be:
#!/bin/bash
parallel ::: ./myscript1.sh ./myscript2.sh
Or even:
#!/usr/bin/parallel --shebang -r
./myscript1.sh
./myscript2.sh
Watch the intro videos to learn more: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Sleep in a while loop gets its own pid

I have a bash script that does some parallel processing in a loop. I don't want the parallel process to spike the CPU, so I use a sleep command. Here's a simplified version.
(while true;do sleep 99999;done)&
So I execute the above line from a bash prompt and get something like:
[1] 12345
Where [1] is the job number and 12345 is the process ID (pid) of the while loop. I do a kill 12345 and get:
[1]+ Terminated ( while true; do
sleep 99999;
done )
It looks like the entire script was terminated. However, I do a ps aux|grep sleep and find the sleep command is still going strong but with its own pid! I can kill the sleep and everything seems fine. However, if I were to kill the sleep first, the while loop starts a new sleep pid. This is such a surprise to me since the sleep is not parallel to the while loop. The loop itself is a single path of execution.
So I have two questions:
Why did the sleep command get its own process ID?
How do I easily kill the while loop and the sleep?
Sleep gets its own PID because it is a process running and just waiting. Try which sleep to see where it is.
You can use ps -uf to see the process tree on your system. From there you can determine what the PPID (parent PID) of the shell (the one running the loop) of the sleep is.
Because "sleep" is a process, not a build-in function or similar
You could do the following:
(while true;do sleep 99999;done)&
whilepid=$!
kill -- -$whilepid
The above code kills the process group, because the PID is specified as a negative number (e.g. -123 instead of 123). In addition, it uses the variable $!, which stores the PID of the most recently executed process.
Note:
When you execute any process in background on interactive mode (i.e. using the command line prompt) it creates a new process group, which is what is happening to you. That way, it's relatively easy to "kill 'em all", because you just have to kill the whole process group. However, when the same is done within a script, it doesn't create any new group, because all new processes belong to the script PID, even if they are executed in background (jobs control is disabled by default). To enable jobs control in a script, you just have to put the following at the beginning of the script:
#!/bin/bash
set -m
Have you tried doing kill %1, where 1 is the number you get after launching the command in background?
I did it right now after launching (while true;do sleep 99999;done)& and it correctly terminated it.
"ps --ppid" selects all processes with the specified parent pid, eg:
$ (while true;do sleep 99999;done)&
[1] 12345
$ ppid=12345 ; kill -9 $ppid $(ps --ppid $ppid -o pid --no-heading)
You can kill the process group.
To find the process group of your process run:
ps --no-headers -o "%r" -p 15864
Then kill the process group using:
kill -- -[PGID]
You can do it all in one command. Let's try it out:
$ (while true;do sleep 99999;done)&
[1] 16151
$ kill -- -$(ps --no-headers -o "%r" -p 16151)
[1]+ Terminated ( while true; do
sleep 99999;
done )
To kill the while loop and the sleep using $! you can also use a trap signal handler inside the subshell.
(trap 'kill ${!}; exit' TERM; while true; do sleep 99999 & wait ${!}; done)&
kill -TERM ${!}

Resources