75

For example,

#!/bin/bash
while :
do
    sl
done

How to terminate this bash script?

Yinyanghu
  • 863

11 Answers11

158
  1. press Ctrl-Z to suspend the script
  2. kill %%

The %% tells the bash built-in kill that you want to send a signal (SIGTERM by default) to the most recently suspended background job in the current shell, not to a process-id.

You can also specify jobs by number or by name. e.g. when you suspend a job with ^Z, bash will tell you what its job number is with something like [n]+ Stopped, where the n inside the square brackets is the job number.

For more info on job control and on killing jobs, run help jobs, help fg, help bg, and help kill in bash, and search for JOB CONTROL (all caps) or jobspec in the bash man page.

e.g.

$ ./killme.sh 
./killme.sh: line 4: sl: command not found
./killme.sh: line 4: sl: command not found
./killme.sh: line 4: sl: command not found
./killme.sh: line 4: sl: command not found
./killme.sh: line 4: sl: command not found
...
...
...
./killme.sh: line 4: sl: command not found
^Z
[1]+  Stopped                 ./killme.sh
$ kill %%
$ 
[1]+  Terminated              ./killme.sh

In this example, the job's number was 1, so kill %1 would have worked the same as kill %%

(NOTE: I don't have sl installed so the output is just "command not found". in your case, you'll get whatever output sl produces. it's not important - the ^Z suspend and kill %% will work the same)

cas
  • 78,579
  • 5
    BTW, for rapidly executing loops like this, ^Z can be a more reliable way to kill the process than ^C. ^C will be sent to the program (sl) being run in the loop, but the script will keep running...and start another sl. If you press ^C a few times really fast, you may get to kill both the sl and the script (if neither of them trap SIGINT). ^Z will suspend the script almost immediately (immediately if you don't count buffered output that is still being printed to your terminal), so you can kill it with kill %% – cas Sep 18 '12 at 02:48
  • Exactly! I have to say "sl is a fun program". This time, it fun me again! @_@ – Yinyanghu Sep 18 '12 at 03:07
  • Only answer that seems to work also when the loop isn't called from a script but from command line directly. – Skippy le Grand Gourou Aug 03 '14 at 10:25
  • How did you find out about %% ? I see no mention of it in the kill manpage. Nor anything about background processes. – Drew Chapin Feb 18 '18 at 05:16
  • 1
    @DrewChapin I can't remember when/where I found out about it - just something I've known "since forever". The bash man page (search for jobspec) says: The symbols %% and %+ refer to the shell's notion of the current job, which is the last job stopped while it was in the foreground or started in the background. The previous job may be referenced using %-. If there is only a single job, %+ and %- can both be used to refer to that job – cas Feb 18 '18 at 06:23
  • @DrewChapin %% is also specified as the current job in the POSIX standard: http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_204 – Anthony Geoghegan Oct 31 '18 at 12:46
84

The program sl purposely ignores SIGINT, which is what gets sent when you press Ctrl+C. So, firstly, you'll need to tell sl not to ignore SIGINT by adding the -e argument.

If you try this, you'll notice that you can stop each individual sl, but they still repeat. You need to tell bash to exit after SIGINT as well. You can do this by putting a trap "exit" INT before the loop.

#!/bin/bash
trap "exit" INT
while :
do
    sl -e
done
Jim Paris
  • 14,337
  • 6
    I don't have it installed to check, but you might also be able to kill it with SIGQUIT from Ctrl-\ – derobert Sep 17 '12 at 17:36
16

If you want ctrl+c to stop the loop, but not terminate the script, you can place || break after whatever command you're running. As long as the program you're running terminates on ctrl+c, this works great.

#!/bin/bash
while :
do
    # ctrl+c terminates sl, but not the shell script
    sl -e || break
done

If you're in nested loop, you can use "break 2" to get out of two levels, etc.

6

The easiest way is to issue the QUIT signal, which is usually attached to Control-Backslash.

When you see the train, hit Control-\

RobertL
  • 6,780
  • That's a new one on me. More details at https://askubuntu.com/questions/341189/why-does-ctrl-backslash-not-kill-the-process-any-more – Mark Hudson Dec 10 '20 at 23:27
5

You can terminate that script by pressing Ctrl+C from terminal where you started this script. Of course this script must run in foreground so you are able to stop it by Ctrl+C.

Or you can find PID (Process ID) of that script in other opened terminal by:

ps -ef | grep <name_of_the_script>
kill -9 <pid_of_your_running_script>

Both ways should do the trick your are asking for.

panaroik
  • 272
  • 1
    It can't be terminated by Ctrl+C from terminal. So I have to kill it by ps or top. I just want to know how to kill it directly! – Yinyanghu Sep 17 '12 at 16:15
4

Another way to terminate the entire script would be to background the sl command and then trap signal INT to kill the entire process group of the script with signal HUP.

#!/bin/bash

trap 'trap - INT; kill -s HUP -- -$$' INT
#trap 'trap - INT; kill -s HUP 0' INT

while :
do
   sl & wait
done
chanze
  • 41
1

You can kill the pid of shell (bash).
I just tried and it works.
Because I cannot see the process from ps -ef (the job that we run in the looping script).

TPS
  • 2,481
Suwarto
  • 11
-1

Put at the end of script:

kill -9 $PPID
exit
Greenonline
  • 1,851
  • 7
  • 17
  • 23
jeira
  • 1
  • This won’t help, the question effectively asks about exiting the loop; adding something after the loop won’t make a difference. – Stephen Kitt Feb 10 '22 at 10:24
-2

use set -e to exit from failure.

#!/bin/bash
set -e
while :
do
    sl
done
HalosGhost
  • 4,790
-2

The killing thing is awful, because you never now, if the script has to run twice. AND your exit code is wrong.

while [ something ]; do

 if [ somethingelse ]; then
   echo shut down script with exit code 0
   exit 0
 fi
done

echo something else not happend
exit 2 # Return val important for example for monitoring

No working. Solution = use perl. while opens own bash

muru
  • 72,889
rene
  • 1
-2
while [ true ] 
do

  #check if script is running
  ps | grep script_name.sh | grep -v grep >/dev/null 2>&1

  if [ "$!" != "0" ] ; then
    break
  else

    kill -9 ` ps -ef | grep script_name.sh | cut -d "a" -f 1` 
    echo "kill -9 `get script PID`"

  fi

done

this should help.

coder007
  • 111