75

For example,

#!/bin/bash
while :
do
    sl
done

How to terminate this bash script?

12
  • 1
    Give more details. Do you want to stop it interactively or programmatically?
    – manatwork
    Sep 17, 2012 at 15:57
  • 2
    you can press ctrl-c to send the SIGINT signal (in most shells) or you can press ctrl-z that sends the SIGTSTP signal (in most shells). In the case you pressed ctrl-z the related process isn't killed but paused. You can resume it with fg (at least in bash) Sep 17, 2012 at 15:59
  • 1
    Use user1146332's suggestion but raise the violence level. I mean, press Ctrl-C and keep it pressed. After a few seconds usually the while which keeps starting newer and newer sl instances will also receive a SIGINT.
    – manatwork
    Sep 17, 2012 at 16:47
  • 5
    The problem is not the script, but the sl command which I believe is an annoing command for those who misspell ls, showing a train passing slowly by. As far as I know it traps the SIGINT signal and must be killed with SIGKILL.
    – user13742
    Sep 17, 2012 at 16:48
  • 1
    Thanks. Not packed for my distro, that's why I not knew/found it. (I think I will not file a request for it.)
    – manatwork
    Sep 17, 2012 at 17:00

11 Answers 11

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)

6
  • 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, 2012 at 2:48
  • Exactly! I have to say "sl is a fun program". This time, it fun me again! @_@
    – Yinyanghu
    Sep 18, 2012 at 3:07
  • Only answer that seems to work also when the loop isn't called from a script but from command line directly. Aug 3, 2014 at 10:25
  • How did you find out about %% ? I see no mention of it in the kill manpage. Nor anything about background processes. Feb 18, 2018 at 5: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, 2018 at 6:23
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
1
  • 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, 2012 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.

0
6

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

When you see the train, hit Control-\

1
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.

1
  • 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, 2012 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
0
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).

-1

Put at the end of script:

kill -9 $PPID
exit
1
  • This won’t help, the question effectively asks about exiting the loop; adding something after the loop won’t make a difference. Feb 10, 2022 at 10:24
-2

use set -e to exit from failure.

#!/bin/bash
set -e
while :
do
    sl
done
1
  • 1
    That won't work. sl doesn't die with ctrl+c. Aug 23, 2018 at 17:38
-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.

1
  • 2
    Why do you think the PID of the last background command will ever be 0?
    – manatwork
    Aug 8, 2013 at 13:18
-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

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .