Pgrep in the shell script: not counting the script itself

grep

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.

Best Answer

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
Related Question