2

I am writing a script that detects if there is any instance of it already running on my Linux machine and shows on screen the number of instances.

The content of the "detect_itself.sh" script is:

#!/bin/sh

INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
echo "Number of detect_itself.sh instances running now =" $INSTANCES_NUMBER
echo "Second method:"
ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
echo "Third method:"
echo `ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`
echo "Please, press a key"
read -r key

When executing the script it shows by screen:

Number of detect_itself.sh instances running now = 2
Second method:
1
Third method:
2
Please, press a key

But I expected it to show:

Number of detect_itself.sh instances running now = 1
Second method:
1
Third method:
1
Please, press a key

I do not understand why if I execute ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l it returns the value 1, but if I save this value in a variable and show it with echo it shows 2.

  • This is because of the subshell created by the command substitution but I don't fully understand why. You will also see two instances if you just run ( ps -ef | grep '[d]etect_itself.sh' ) within your script. – Jesse_b May 30 at 13:28
  • Additionally FYI: ps -ef | grep '[d]etect_itself.sh' is equivalent to ps -ef | grep detect_itself.sh | grep -v -i grep and when using grep you can eliminate wc -l by just using ps -ef | grep -c '[d]etect_itself.sh'. (Not really related to your issue though) – Jesse_b May 30 at 13:30
  • 1
    ... probably simpler to use pgrep -cf detect_itself.sh if it's available – steeldriver May 30 at 13:37
  • FWIW I suspect it's the subshelling due to the pipes rather than that due to the command substitution per se - compare echo $(pgrep -cf 'detect_itself.sh') (or change the script to use ksh instead of sh) – steeldriver May 30 at 13:42
  • 1
    @Jesse_b yeah, - confusing isn't it? that's why I posted a comment rather than an answer... – steeldriver May 30 at 13:45
1

This is happening because you are running the ps command in a subshell. When you run this:

INSTANCES_NUMBER=`ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`

That actually forks a new subshell to run that command in. Because this fork is a a copy of the parent, there are now two detect_itself.sh instances running. To illustrate, run this:

#!/bin/sh
echo "Running the ps command directly:"
ps -ef | grep detect_itself.sh | grep -v -i grep
echo "Running the ps command in a subshell:"
echo "`ps -ef | grep detect_itself.sh | grep -v -i grep`"

That should print:

$ test.sh
Running the ps command directly:
terdon   25683 24478  0 14:58 pts/11   00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
Running the ps command in a subshell:
terdon   25683 24478  0 14:58 pts/11   00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh
terdon   25688 25683  0 14:58 pts/11   00:00:00 /bin/sh /home/terdon/scripts/detect_itself.sh

Happily, there's an app for that! This sort of thing is precisely why pgrep exists. So change your script to:

#!/bin/sh
instances=`pgrep -fc detect_itself.sh`
echo "Number of detect_itself.sh instances running now = $instances"
echo "Second method:"
ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l
echo "Third method (wrong):"
echo `ps -ef | grep detect_itself.sh | grep -v -i grep | wc -l`

That should print:

$ detect_itself.sh
Number of detect_itself.sh instances running now = 1
Second method:
1
Third method (wrong):
2

IMPORTANT: this isn't a safe thing to do. For example, if you have a script called this_will_detect_itself, that will be counted. if you have the file opened in a text editor, that will also be counted. A far more robust approach for this sort of thing is to use a lockfile. Something like:

#!/bin/sh

if [[ -e /tmp/I_am_running ]]; then
    echo "Already running! Will exit."
    exit
else
    touch /tmp/I_am_running
fi
## do whatever you want to do here

## remove the lock file at the end
rm /tmp/I_am_running

Or, even better, look into using trap to make sure the file is removed even when the script crashes. The details will depend on what exactly you want to do, on why you need to detect the running instance.

  • 1
    Note that depends on the sh implementation. For instance ksh93 doesn't implement subshells with forks, yash would run wc in the subshell process. But even in those, if the process running ps gathers the process information before any of the other processes in the pipeline execute their respective commands, you'd see more instances of detect_itself.sh – Stéphane Chazelas Jun 4 at 7:20
  • Strictly speaking that should be pgrep -fc 'detect_itself\.sh as . is a regular expression operator. – Stéphane Chazelas Jun 4 at 7:22

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.