Kill all background jobs

Is there a more compact form of killing background jobs than:

for i in {1..5}; do kill %$i; done

Also, {1..5} obviously has a hard-coded magic number in it, how can I make it “N” with N being the right number, without doing a:

$(jobs | wc -l)

I actually use j in PS1 to get the # of managed jobs, is this equivalent?

Asked By: Robottinosino

||

To just kill all background jobs managed by bash, do

kill $(jobs -p)

Note that since both jobs and kill are built into bash, you shouldn’t run into any errors of the Argument list too long type.

Answered By: jw013

I prefer to check if there’s any jobs that exist before killing them – this way the script won’t fail if there’s nothing running.

It’s also shorter to type. Throw this in your .bash_profile:

function killjobs () {
    JOBS="$(jobs -p)";
    if [ -n "${JOBS}" ]; then;
        kill -KILL ${JOBS};
    fi
}

Then run:

killjobs

To kill any jobs running.

Answered By: mikemaccana

Use xargs instead of the $(jobs -p) subcommand, because if jobs -p is empty then the kill command will fail.

jobs -p | xargs kill
Answered By: brunocascio

Seems like jobs -p | xargs kill does the job, however it produces some unwanted output piped to kill. Here I am grouping output of jobs -p by having/not having + or – character

function killjobs() {
    JOBS=$(jobs -p)
    echo $JOBS | grep -v "+|-"| awk '{print $2}' | xargs kill -9
    echo $JOBS | grep "+|-"| awk '{print $3}' | xargs kill -9
}

And later you can just call killjobs

Answered By: samvel1024

I have several backgrounded compound commands I want to terminate gracefully, by sending SIGINT to each process, on macOS. None of the other solutions worked properly for that, so I came up with this:

jobs -p | xargs -n1 pkill -SIGINT -g

jobs -p lists background processes started by the current shell.

xargs -n1 executes pkill once for each job.

pkill -SIGINT -g sends SIGINT (same as ctrl+c) to all processes in the process group.

Answered By: Clement Cherlin

I guess depending on what output jobs -p gives, the solution could be slightly different. In my case

$ jobs -p
[1]  - 96029 running    some job
[2]  + 96111 running    some other job

Therefore, doing the following is no good.

$ jobs -p | xargs kill
kill: illegal process id: [1]

On the other hand, running kill $(jobs -p) does work but entails a lot of error messages, since the non-PID strings get passed to kill as well.

Therefore, my solution is to grep the PID first and then use xargs, as follows:

$ jobs -p | grep -o -E 'sd+s' | xargs kill
Answered By: Fanchen Bao

In bash and zsh I would use something like this (after considering the comment from pizdelect about the process group):

jobs -p | extract_pids | xargs --no-run-if-empty kill -TERM -- || true

where

extract_pids() {
    awk '{
        if (NF == 1) { print -$1 }
        else if (NF > 1) {
            if ($2 == "+" || $2 == "-") { print -$3 }
            else { print -$2 }
        }
    }'
}

As far as I tested jobs -p returns the PIDs in bash, f.i.:

$ jobs -p
41659

but can have these formats in zsh, f.i.:

$ jobs -p
[1]    206 running    sleep 10
[2]  - 208 running    sleep 10
[3]  + 210 running    sleep 10

extract_pids tries to handle these (4) cases.

Note: checking the processes with jobs -p can return PIDs, which are already gone at the time xargs kill is invoked, so we ignore errors with a trailing || true.

Answered By: András

@pizdelect point is what brought me here. Jobs spawned using pipes have multiple pids. jobs -p is only going to provide the first.

for j in $(jobs | awk '{gsub("[^0-9]","",$1);printf "%%%sn", $1}');do kill $j;done

If you want to get them all, you have to use the job ID.

Answered By: Frobozz

The accepted answer by @jw013 works in most situations, except when a job has background jobs since only the group leader is killed. (@pizdelect identified this limitation in this comment.)

@pizdelect also pointed out that kill accepts negative PID values to choose the whole process group in this comment.

Furthermore, one might not want to kill Stopped jobs, hence jobs -r might be useful.

Combining all these elements, here’s a solution that kills all running jobs, including background jobs:

for i in $(jobs -rp); do kill -- "-$i"; done

Here’s a solution that kills all jobs, including background jobs:

for i in $(jobs -p); do kill -- "-$i"; done

Answered By: Daniel Le

Processes can ignore some signals. However, SIGKILL can not be ignored nor caught to do cleanups. To ensure all background jobs managed by bash are killed, try,

kill -9 $(jobs -p)
Answered By: Shogun

As others have said already, jobs -p | xargs kill or kill $(jobs -p) is wrong as:

  • jobs -p prints the process group ids, with extra information with zsh
  • kill number kills the process with number as id, not process group. There’s not even a guarantee that the process group ids returned by jobs -p will have a corresponding running process by the same number. And even if there is, kill will not kill the other processes in the job / process-group
  • if there’s no job in the job table, kill will be run without arguments causing an error.

Here you want to run either kill -- -<pgid> or kill %<jobnumber> for all the jobs.

With bash or other shells that only print the pgid upon jobs -p, you can do:

jobs -p | sed 's/^/-/' | xargs -r kill --

(-r being a GNU extension)

Note that it doesn’t work in dash where jobs running in a subshell (because as part of a pipeline) only lists the jobs of that subshell. There, you could instead redirect the output of jobs -p to a temporary file and then run sed|xargs on it, but anyway dash is not really intended to be run interactively.

With zsh:

() { (($#)) && kill %${^@}; } ${(k)jobstates}

Where we pass the list of job numbers (the keys of the $jobstates special associative array) to an anonymous function that runs kill with % prepended to the job number if its number of arguments is non-zero.

Answered By: Stéphane Chazelas

Do the same, faster:

jobs -p | xargs -P100 kill

Runs 100 simultaneous kill threads

Answered By: Roel Van de Paar
kill_all_jobs () {
local jobs=$(jobs -r | sed -En 's/^[([0-9]+)].*$/%1/p')
[ -n "$jobs" ] && kill $jobs
return 0
}
  • jobs will also list jobs that have finished since the last time jobs were checked,¹ -r will exclude them.²
  • The sed script will do the following:
    • Match each line against the regular expression using the “extended” syntax (-E) ^[([0-9]+)].*$ ([digits]anything),
    • if it does not match, print nothing (-n),³
    • if it does, replace (s/) the match (which will be the entire line because of ^…$) with %1 (%digits) and do print the resulting line (/p). (Bash’s builtin kill will recognize %jobnumber as referring to that job.)
  • We only run kill if $jobs is not empty, because running kill with no arguments would be an error. (Note that we cannot use xargs -r kill here because xargs can only invoke /usr/bin/kill, not Bash’s builtin kill, and the former does not know about the shell’s jobs.)
  • The return 0 prevents the function from returning 1 if there were no jobs, which would usually cause the shell to exit in set -e mode.

¹ Note that “checking jobs” does not necessarily mean “invoking jobs”: Apparently, an interactive shell will usually do so before each “primary prompt” (prompt for the next command), as casually mentioned by the documentation for Bash’s set -b. (I couldn’t find another mention of this in man bash…) To verify this, run sleep 1 & and hit Enter again after at least one second: [1]+ Done sleep 1 appears before the prompt. And to verify that this line is indeed included in the output of jobs without -r, compare the results of output=$(jobs)Enterecho "$output" and output=$(jobs -r)Enterecho "$output" (each at least one second after sleep 1 &). (Mysteriously, the line will appear both before the next prompt and in the variable with output=$(jobs), but only in the file with jobs > somefile.)
² It will also exclude jobs paused with SIGSTOP, but I haven’t found a way to list them without including finished jobs, and sending those jobs any signal other than SIGKILL or SIGCONT won’t do anything in the short-term, anyway. …Nope, using Bash’s builtin kill in conjunction with %jobnumber seems special in this regard: It can terminate a paused sleep inf & even with SIGTERM. Maybe it also sends SIGCONT to a paused job? I couldn’t find this documented anywhere…
³ Every non-empty line printed by jobs should match this pattern as per POSIX, but just in case…

Answered By: Ground
Categories: Answers Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.