2

I have a script like this:

pid = $(ps -ef | grep my-service | awk '{print $2}')

Which gives me a PID of the process running my-service. I want to update my script to handle multiple processes running, and to kill all old my-service processes except the most recent one. But if there is only one process run, just kill it. How do I do that?

2
  • 1
    Does pgrep -f my-service matches your process?
    – thanasisp
    Oct 30, 2020 at 18:46
  • Yes it does. I'll update that.
    – George
    Oct 30, 2020 at 18:48

2 Answers 2

3

pgrep (and pkill) has some useful parameters for this task, like -c (--count) to count the number of processes and -n (--newest) to get only the newest pid. (man pgrep for more)

So we could use this case statement. For no pids returned, we exit, for one we kill this process. If more than one processes are found, we exclude the newest pid with a grep -v against all the pids and then pipe to kill.

#!/bin/bash

die="my-service"

case $(pgrep -cf "$die") in
    0) exit 0 ;;
    1) pkill -f "$die" ;;
    *) grep -v $(pgrep -nf "$die") <(pgrep -f "$die") | xargs kill ;;
esac

Note: In the above, -f parameter is used, this is matching the full command, it should not be used if we want to match only the process name. From man pgrep:

-f, --full

The pattern is normally only matched against the process name. When -f is set, the full command line is used.

2

Assuming procps-ng's ps and an unmodified $IFS, you could do:

set -- $(ps -o pid= --sort=-start_time -C my-service)
case $# in
  (0) ;;
  (1) kill "$1";;
  (*) shift; kill "$@"
esac

set assigns the positional parameters ($1, $2...) to the pids of processes whose name is my-service, sorted in reverse by start_time (most recently started first).

Then based on their number ($#), we do nothing if it's 0, kill it if it's 1, and for any other number, shift to discard the youngest, and kill the rest.

(note that ps -C name matches exactly on the full process name, not on the arg list reported by ps -f)

5
  • 1
    What's the difference when using () or ) for case? I see in the manual it is described only in the syntax pattern.
    – thanasisp
    Oct 31, 2020 at 17:38
  • 1
    @thanasisp, it's mostly a matter of taste. The Bourne shell only did ). POSIX specifies both. IIRC ) trips some shells when the case statement is inside $(...) (which the Bourne shell didn't have either). I much prefer (). Oct 31, 2020 at 17:54
  • 1
    @thanasisp, see also the POSIX rationale section about that. Oct 31, 2020 at 18:09
  • Wow, this was absolutely brilliant. Could you explain a bit, what the first line is doing with set --?
    – Rakib Fiha
    Nov 1, 2020 at 5:30
  • 1
    @RakibFiha, see edit. Nov 1, 2020 at 8:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.