20

I want to run two commands simultaneously in bash on a Linux machine. Therefore in my ./execute.sh bash script I put:

command 1 & command 2
echo "done"

However when I want to stop the bash script and hit Ctrl+C, only the second command is stopped. The first command keeps running. How do I make sure that the complete bash script is stopped? Or in any case, how do I stop both commands? Because in this case no matter how often I press Ctrl+C the command keeps running and I am forced to close the terminal.

1
  • Make the follow up question into a separate question and remove here. Now it is unclear for a visitor if both question was answered, or just the intial question.
    – Zelda
    Jan 7, 2014 at 8:59

4 Answers 4

21

If you type

command 1 & command 2

this is equal to

command 1 &
command 2

i.e. this will run the first command in background and then runs the second command in foreground. Especially this means, that your echo "done" is printed after command 2 finished even if command 1 is still running.

You probably want

command 1 &
command 2 &
wait
echo "done"

This will run both commands in background and wait for both to complete.


If you press CTRL-C this will only send the SIGINT signal to the foreground process, i.e. command 2 in your version or wait in my version.

I would suggest setting a trap like this:

#!/bin/bash

trap killgroup SIGINT

killgroup(){
  echo killing...
  kill 0
}

loop(){
  echo $1
  sleep $1
  loop $1
}

loop 1 &
loop 2 &
wait

With the trap the SIGINT signal produced by CTRL-C is trapped and replaced by the killgroup function, which kills all those processes.

2
  • Thank you for your answer! I do however have a follow-up question. I now have the following script: trap killgroup SIGINT killgroup(){ echo killing... kill 0 } command001 command1 & command2 I have ommitted the wait command because the script is allowed to continue when command2 is finished. However it seems that command 1 already starts when I run the script. Can I fix this? Thanks
    – maero21
    Jan 3, 2014 at 22:35
  • command1 starts directly after command001 is finished. you can use set -x at the beginning of your script to print the commands that are executed.
    – michas
    Jan 4, 2014 at 0:03
7

When putting a command into the background from a script, the PID is not displayed on the screen. You can use the builtin variable $! which stores the PID of the last process so you can capture the PID of command1.

command1 &
echo $!

would echo the PID of command1.

Bash also provides the trap builtin which you can use to register a sequence of commands to run when particular signals are received. You can use this to catch the SIGINT and kill command1 before exiting the main script e.g.

#!/bin/bash

onINT() {
echo "Killing command1 $command1PID too"
kill -INT "$command1PID"
exit
}

trap "onINT" SIGINT
command1 &
command1PID="$!"
comamnd2
echo Done

Now, whilst command 2 is running, hitting Ctrl C will cause both command1 and command2 to be sent SIGINT.

3
  • You can also use %n syntax to kill specific background jobs in this shell. Usually you issue kill %1 to kill the latest and the only background process. With more background processes, use jobs to see the list first.
    – 9000
    Jan 1, 2014 at 15:29
  • @9000: Yes but the OP is running their commands in a script (./execute.sh) so getting jobs to work is much more problematic surely ?
    – user591
    Jan 1, 2014 at 15:33
  • @lain: certainly. I missed the point about the running from a script.
    – 9000
    Jan 1, 2014 at 16:50
5

Newer versions of GNU Parallel will do what you want:

parallel ::: "command 1" "command 2"
echo "done"

Or if command remains the same:

parallel command ::: 1 2
echo done

Watch the intro video for a quick introduction: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Walk through the tutorial (man parallel_tutorial). You command line with love you for it.

2

Ctrl + C sends a SIGINT signal to your front process, which is command2. command1 is run in background, therefore not concerned by the input stream.

When you type command1 &, bash should give you the process' PID, something like [1234]. To kill this process, you can use kill 1234. Now, if you have the PIDs of both command1 and command2 (have a look at ps -ef), you can use kill to terminate them all :

kill pid1 pid2 pid3 ...

A little trick would be to run both commands in background, with :

command1 & command2 &

Bash will give you both PIDs, ready to get killed. Another trick would be to bring back command1 in the foreground once you killed command2 :

command1 & command2
# Hit Ctrl+C : command2 terminates.

fg # Bring back command1 to foreground.
# Hit Ctrl+C again, command1 terminates.

More info available here : http://linuxg.net/how-to-manage-background-and-foreground-processes/

You must log in to answer this question.

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