16

As in the title - how to kill all background processes in zsh?

d33tah
  • 10,999
  • 13
  • 68
  • 158

5 Answers5

18
alias killbg='kill ${${(v)jobstates##*:*:}%=*}'

. It is zsh, no need in external tools.

If you want to kill job number N:

function killjob()
{
    emulate -L zsh
    for jobnum in $@ ; do
        kill ${${jobstates[$jobnum]##*:*:}%=*}
    done
}
killjob N
ijoseph
  • 6,505
  • 4
  • 26
  • 26
ZyX
  • 52,536
  • 7
  • 114
  • 135
  • 1
    @ZachRiggle It is in `man zshexpn` and `man zshmodules`: `$jobstates` is a associative array parameter, `(v)` selects only values from this array, `#` makes zsh remove given pattern starting from the start of string, selects least lengthy pattern for removal, `*:*:`: pattern that makes zsh remove first two colon-separated fields of each value (`#` on array parameters is applied to each value), `%` is like `#`, but for end of strings and `=*` makes zsh remove everything after the last eq sign including the sign itself. Each `$jobstates` value looks like `job-state:mark:pid=state...`. – ZyX Sep 05 '14 at 14:58
  • It appears I have a bug here: it does not work if the whole pipe was suspended. – ZyX Sep 05 '14 at 15:00
  • 1
    **Update**: changed `#` to `##`: this way it will remove everything up to the last colon, effectively leaving only last process in the pipe. I am working under an assumption that last one will always be alive, otherwise more complex computations should be performed. – ZyX Sep 05 '14 at 15:07
7

one should use the builtin zsh built-in command alongside with the other kill zsh built-in command as:

builtin kill %1

as kill is also a separate binary file from util-linuxpackage (upstream, mirror) located in /usr/bin/kill which does not support jobs (kill: cannot find process "%1").

use keyword builtin to avoid name conflict or enable the kill built-in if it is disabled.


there is a concept of disabling and enabling built-in commands (ie. shell's own commands such as cd and kill ) in shells, and in you can enable (a disabled) kill builtin as:

enable kill

issue disable to check if the builtin is disabled (and enable to see the enabled ones).

  • The question was how to kill **_all_** background processes. If you have 100s, doing it one-by-one ain't gonna cut it, however useful it is to know about this `builtin` vs. file conflict namespace conflict. – ijoseph Dec 16 '22 at 18:02
2

Minor adjustment to @Zxy's response...

On my system, I found that suspended jobs weren't killed properly with the default kill signal. I had to actually change it to kill -KILL to get suspended background jobs to die properly.

alias killbg='kill -KILL ${${(v)jobstates##*:*:}%=*}'

Pay special attention to the SINGLE QUOTES around this. If you switched to double quotes, you would need to escape the each "$". Note that you can NOT use a function to wrap this command since the function will increment the $jobstates array causing the function to try killing itself... Must use an alias.

The killjob script above is a bit redundant since you can just do:

kill %1

Less keystrokes and it's already build into zsh.

erwin
  • 442
  • 6
  • 13
1

This works for both ZSH and Bash:

: '
killjobs - Run kill on all jobs in a Bash or ZSH shell, allowing one to optionally pass in kill parameters

Usage: killjobs [zsh-kill-options | bash-kill-options]

With no options, it sends `SIGTERM` to all jobs.
'
killjobs () {

    local kill_list="$(jobs)"
    if [ -n "$kill_list" ]; then
        # this runs the shell builtin kill, not unix kill, otherwise jobspecs cannot be killed
        # the `$@` list must not be quoted to allow one to pass any number parameters into the kill
        # the kill list must not be quoted to allow the shell builtin kill to recognise them as jobspec parameters
        kill $@ $(sed --regexp-extended --quiet 's/\[([[:digit:]]+)\].*/%\1/gp' <<< "$kill_list" | tr '\n' ' ')
    else
        return 0
    fi

}

@zyx answer didn't work for me.

More on it here: https://gist.github.com/CMCDragonkai/6084a504b6a7fee270670fc8f5887eb4

CMCDragonkai
  • 6,222
  • 12
  • 56
  • 98
0
alias killbg='for job in \`jobs -l | egrep -o "([0-9][0-9]+)"`; 
d33tah
  • 10,999
  • 13
  • 68
  • 158
  • Super! Now try suspending `echo 123 | less` and check out what it will kill. Hint: by the time you run `kill` `echo 123` process will already be dead and its PID may already have been taken. So with such suspended pipe with your `killbg` you may kill 2 innocent processes: process `123` (`echo` argument) and process that happened to retake `echo` PID. – ZyX Sep 05 '14 at 15:03
  • My solution will fail in this case though (it will try to kill `{echoPID}=done:{lessPID}`). – ZyX Sep 05 '14 at 15:04