1

Hello there long history short: I'm trying to create a script to run through CI server to kill a specific process.

So searching over the internet I found out this command:

myuser@server:/home/myuser# ps -ef | grep someUniqueText | grep -v grep | awk '{print $2}'
5263

When I run it in the console it returns me exactly what I need (5263), so I created this bash:

#!/bin/bash

PROCESSNAME=$1
PID=$(ps -ef | grep $PROCESSNAME | grep -v grep | awk '{print $2}');

echo $PID;

When I execute it as:

myuser@server:/home/myuser# ./killProcess.sh somename
5263 4587 1236

The bash script returns 3 numbers being the first the process I need and I have no idea what the other two are, Why? o.O

PS.: I'm using echo to test it first, I will change it to kill -9

  • Are you aware of the (exising) killall tool? – PerlDuck May 11 '18 at 16:29
  • Probably the script finds itself because somename is also part of your commandline. – PerlDuck May 11 '18 at 16:31
  • @PerlDuck No, I was not aware of killall (what defines a process name?). And, for your second comment, doesn't explain why it runs fine in the command line though – Jorge Campos May 11 '18 at 16:33
  • Also pgrep and pkill – glenn jackman May 11 '18 at 16:41
  • 1
    You could also grep away $0 – which is the script's name as you call it, i.e. including the ./ part, if present: PID=$(ps -ef | grep $PROCESSNAME | grep -v grep | grep -v $0 | awk '{print $2}');. But killall somename would also work. There's even a preview: killall -s 0 -v somename. It "sends" signal 0 which simply checks whether the process exists but doesn't actually send a signal. The process won't even notice. – PerlDuck May 11 '18 at 16:59
2

When running ./killProcess.sh somename then a process similar to

/bin/bash ./killProcess.sh somename

exists and the ps | grep | grep | awk-line also finds this one because it has the text somename in it. I'm not exactly sure why you get two additional PIDs (and not just one) but I reckon it is some parent/child thingy between bash and killProcess.sh. In one line the PID shows up as PID and in the other one as PPID.

You can avoid these PIDs by grepping them away based on the script's name. It is stored in $0, so

PID=$(ps -ef | grep $PROCESSNAME | grep -v grep | grep -v $0 | awk '{print $2}');

or (with fewer program calls):

PID=$(ps -ef | grep $PROCESSNAME | grep -v -e grep -e $0 | awk '{print $2}');

Another (safer) way would be to grep away the script's PID. It is stored in $$:

PID=$(ps -ef | grep $PROCESSNAME | grep -v -e grep -e $$ | awk '{print $2}');

Or do it all in awk:

PID=$(ps -ef | awk "/$PROCESSNAME/ && !/awk/ && !/$$/ {print \$2}");

We need to escape the \$2 here so it isn't interpreted by the shell but by awk. That command means:

If the line contains $PROCESSNAME and does not contain awk and does not contain our very own PID, then print the second column.

The advantage is (in theory) that this runs faster because only one additional command (awk) is required as opposed to 3 greps and an awk.

But:

All these greps and awks aren't perfect and you'll always find a situation we haven't thougt of here (like partial matches etc.) For that reason I recommend using one of the already written tools:

  • Very well explained. Thank you sir! – Jorge Campos May 11 '18 at 18:11
2

I once implemented a similar idea, as a shell function, that I call psg (for "ps|grep")

psg() {
    local -a patterns=()
    (( $# == 0 )) && set -- $USER
    for arg do 
        patterns+=( "-e" "[${arg:0:1}]${arg:1}" )
    done
    ps -ef | grep "${patterns[@]}"
}

This uses a trick of putting the first character of the pattern in brackets, so you get

ps -ef | grep "[s]omename"

This allows you to drop the grep -v grep part of the pipeline.

If you don't pass any arguments, it uses your username to search for your processes.

  • It is not working. It prints nothing. Should the process be running with the user that is calling the script? – Jorge Campos May 11 '18 at 16:56
  • How are you ruining it? It's a shell function so you don't put it in a file and call it like a script: paste it at a shell prompt and call it like psg somename – glenn jackman May 11 '18 at 17:29
  • 3
    s/ruining/running/ – glenn jackman May 11 '18 at 17:34
  • 1
    Ooh... Ok, thanks for clarifying. But that is not what I need. I will be calling this over a ssh command through a CI server (not sure how to do that with your version). I figured out how to fix my script with PerlDuck's help. – Jorge Campos May 11 '18 at 17:34
  • 2
    @glenn You did that typo on purpose, didn't you? ;-) I like it. – PerlDuck May 11 '18 at 17:53

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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