4

This is the behavior of pgrep on my FreeBSD script:

luis@Balanceador:~/Temporal$ cat test.sh
#!/usr/bin/env bash
pgrep -fl "test.sh"

luis@Balanceador:~/Temporal$ ./test.sh
luis@Balanceador:~/Temporal$

No output. This is: the script itself is not detected as running. Just like the command-line behavior of pgrep. This is fine for me.

And this is the Linux (Ubuntu) case:

luis@Terminus:~/Temporal$ cat test.sh
#!/usr/bin/env bash
pgrep -fl "test.sh"

luis@Terminus:~/Temporal$ ./test.sh
4514 bash
luis@Terminus:~/Temporal$

As can be seen, the test.sh script itself seems to be detected as a running process.

I am developing a shell (Bash) script that must be able to stop and wait if another instance of it (same name) is detected, and must work on both Linux and FreeBSD, so I would like to homogeneize the pgrep command for this.

Is there any way to make both of them behave the same?

Answers for any derivative like grep or ps accepted.

Tested:

The -x (exact) switch from the Man Page, but does not work with parameters on the scripts (or I don't understand how to make it work). Parameters are not important to detect (they could be whatever) in this case; I just need to detect the main '.sh' script.

Further Notes:

  • Please, don't answer like "Use file locking". I actually want to know about the pgrep tool.
1
  • What do you want to happen in case multiple instances of the script are running? Sep 18, 2022 at 12:50

2 Answers 2

6

FreeBSD's pgrep excludes ancestors of the pgrep process, including the script instance that ran it. Linux's pgrep doesn't have a corresponding feature.

You can exclude the script instance manually. The process id to exclude is in $$. You do need to be a bit careful to also avoid any subshell: the most straightforward method pgrep … | grep -v "^$$ " might list the right-hand side of the pipe if pgrep happens to reach it in the process list before grep is invoked.

instances=$(pgrep -fl "test.sh")
if [[ "$instances" =~ (.*)$'\n'"$$ "[^$'\n']*$'\n'(.*) ]]; then
  instances=${BASH_REMATCH[1]}$'\n'${BASH_REMATCH[2]}
elif [[ "$instances" =~ (^|.*$'\n')"$$ "[^$'\n']*($|$'\n'.*) ]]; then
  instances=${BASH_REMATCH[1]}${BASH_REMATCH[2]}
fi
2
  • Your trick of excluding process id with $$ worked like a charm.
    – Pat
    Dec 6, 2017 at 21:28
  • Or get the pgrep output before starting grep with printf "%s\n" "$(pgrep -fl test.sh)" | grep -v "^$$ " or grep -v "^$$ " <<<"$(pgrep -fl test.sh)" Dec 15, 2017 at 12:19
0

Just add x to the flags:

#!/usr/bin/env bash                                                                                                                                                                                                                            
pgrep -flx "pgrep.sh"

Tested in Ubuntu and it doesn't print anything to the output (if this is what you want!).

4
  • But according to the man page, that forces parameters concidence, and I don't want that. Jun 18, 2016 at 9:17
  • Which way do you want? FreeBSD be like Ubuntu or the other way?
    – Vombat
    Jun 18, 2016 at 9:19
  • Well, both methods are accepted. I would personally prefer the FreeBSD way (more logical), but the real disturbance in a program are the differences (you know: code must have too much if...then sentences). Jun 18, 2016 at 16:39
  • Use a combination of ps and grep. ps -ef | grep -v grep | grep -v pgrep.sh
    – Vombat
    Jun 18, 2016 at 17:00

You must log in to answer this question.

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