0

I was trying to prevent a script from being run by more than one user simultaneously and did not want to use commands only available on some OS'es or shells (pgrep, pidof, ...) and bumped into an issue that I am not sure whether it is a bug or not...

Please ignore the specifics I used in my script: the issue is about the command substitution in bash when using ps.

When I run the following (note the shebang in ksh):

#!/bin/ksh

CMD=`basename $0`
echo $CMD
ps -ef | grep "$CMD"
ps -ef | grep "$CMD" | wc -l
RUNS=`ps -ef | grep "$CMD" | wc -l`
echo $RUNS
if [ $RUNS -gt 2 ]; then
  echo The script is currently being run by another user.
  #exit 1
fi
RUNS=`ps -ef | grep "$CMD"`
echo "$RUNS"
RUNS=`echo "$RUNS" | wc -l`
echo $RUNS
if [ $RUNS -gt 2 ]; then
  echo The script is currently being run by another user.
  #exit 1
fi
ps -ef | grep "$CMD" | wc -l > lock
RUNS=`cat lock`
echo $RUNS
if [ $RUNS -gt 2 ]; then
  echo The script is currently being run by another user.
  exit 1
fi

I get this correct output:

testksh.sh7
abriere  19126  5669  0 14:15 pts/21   00:00:00 /bin/ksh ./testksh.sh7
abriere  19129 19126  0 14:15 pts/21   00:00:00 grep testksh.sh7
2
2
abriere  19126  5669  0 14:15 pts/21   00:00:00 /bin/ksh ./testksh.sh7
abriere  19137 19126  0 14:15 pts/21   00:00:00 grep testksh.sh7
2
2

I get this after replacing the shebang for bash and renaming the script accordingly:

testbash.sh7
abriere   5631  5669  0 14:12 pts/21   00:00:00 /bin/bash ./testbash.sh7
abriere   5634  5631  0 14:12 pts/21   00:00:00 grep testbash.sh7
2
3
The script is currently being run by another user.
abriere   5631  5669  0 14:12 pts/21   00:00:00 /bin/bash ./testbash.sh7
abriere   5643  5631  0 14:12 pts/21   00:00:00 /bin/bash ./testbash.sh7
abriere   5645  5643  0 14:12 pts/21   00:00:00 grep testbash.sh7
3
The script is currently being run by another user.
2

Note the extra line in the ps output.

The following line in bash:

RUNS=`ps -ef | grep "$CMD" | wc -l`

does not return the same value as:

ps -ef | grep "$CMD" | wc -l

Ksh does not have this issue.

As you can see, there are workarounds: I use one in the last section of my script.

I ran the scripts on Linux, AIX and SunOS and they gave me the same results; only Cygwin did not, but the ps command does not return the script in either shell.

Is this a bug? Even if bash runs command substitution within a subshell (see question 21331042), I still consider the variable assigned the value of the command substitution should return the same value as the command itself...

rici
  • 234,347
  • 28
  • 237
  • 341
brierand
  • 9
  • 2
  • Most utilities with the same kind of constraints use the same strategy: create a `xxx.pid` temporary file with the PID of the currently running process. When the utility starts it checks the existence of the temporary file and stops if it exists. Is there a reason in your case for not using this well-known technique? – Renaud Pacalet Sep 12 '15 at 19:07
  • It is not a bug in that this is accepted action. There are a number of other differences between ksh and bash, and yes, different results. For example the behaviour of a `for` loop at one end of a pipe - in bash each end run in child processes. See also http://stackoverflow.com/questions/14686872/sub-shell-diffferences-between-bash-and-ksh – cdarke Sep 12 '15 at 19:16
  • @cdarke: I agree that it is not a bug. Afaics, Posix doesn't require a subshell to be a separate process. I think your linked question is a dup, but I have a little doubt so I don't want to apply the Hammer; I'd suggest you either vote to close or make your comment an answer. – rici Sep 12 '15 at 19:25
  • Yes, under bash, the subshell gets its own process id. Under bash+linux, run `echo "$(ps f)" | less` and then search for `ps f` and you can see all the processes that bash creates to run the command substitution. – John1024 Sep 12 '15 at 19:25
  • It's not like `ps -ef` is portable, either. – tripleee Sep 12 '15 at 21:46

0 Answers0