views:

452

answers:

5

Hello, now this is embarrassing. I'm writing quick script and I can't figure out why this statement don't work.

if [ $(pidof -x test.sh | wc -w) -eq 1 ]; then echo Passed; fi

I also tried using back-ticks instead of $() but it still wouldn't work.

Can you see what is wrong with it? pidof -x test.sh | wc -w returns 1 if I run it inside of script, so I don't see any reason why basically if [ 1 -eq 1 ] wouldn't pass.

Thanks a lot!

+2  A: 

Ah, the real answer: when you use a pipeline, you force the creation of a subshell. This will always cause you to get an increased number:

#!/bin/bash

echo "subshell:"
np=$(pidof -x foo.bash | wc -w)
echo "$np processes"   # two processes

echo "no subshell:"
np=$(pidof -x foo.bash)
np=$(echo $np | wc -w)
echo "$np processes"   # one process

I'm honestly not sure what the shortest way is to do what you really want to. You could avoid it all by creating a lockfile - otherwise you probably have to trace back via ppid to all the top-level processes and count them.

Jefromi
Thanks for your answer. Playing around with your example, I see the problem with my script is actually with `wc -w` part of the script and not the subshell spawning. It returns n+1, which is interesting.
Andrew
@Andrew: er, `wc -w` definitely counts correctly. Just try "echo 1352 | wc -w" and you can easily see it counts one pid as one word.
Jefromi
Since all the actors can cooperate (they are instances of the same script), a lockfile seems like the way to go. I like *lockfile* http://linux.die.net/man/1/lockfile from the *procmail* package http://www.procmail.org/
Chris Johnsen
@Jefromi Like I said in Kevin's answer comment, when I redirect `pidof` output to file and then use on that file `wc -w` it returns correctly 1. If I use `pidof test.sh | wc -w` it returns 2...
Andrew
@Andrew: Yes, you're spawning a subshell when you use the pipeline, but not when you use the redirect. Read my answer again.
Jefromi
@Jefromi aha! Somehow I missed that. Thanks! In this case proabably do it by redirecting pidof to variable and then `echo $var | wc -w` in if statement. Using lockfile for simple checking before rest of the script execution seems like overkill for me...
Andrew
@Andrew: If you really want to do it by pid checking, you don't have to use a temp file - just compare to two instead of one, and document why you do it. (Temp files are unnecessary overhead)
Jefromi
@Chris Johnsen: Your main issue with `lockfile` is dealing with stuck locks given that you may well be holding the lock for an extended period. OK, `lockfile` *does* contain code to work around the problem, but assumes that lock periods are basically “short”.
Donal Fellows
+1  A: 

Jefromi is correct; here is the logic I think you want:

#!/bin/bash
# this is "test.sh"

if [ $(pidof -x test.sh| wc -w) -gt 2 ]; then 
    echo "More than 1"
    exit
fi

echo "Only one; doing whatever..."
Kevin Little
The problem with this is that it's not going to work everywhere in your code - if you place this within a looping construct which also creates a subshell, you'll get three counts. Put that inside another loop, four counts.
Jefromi
True. But, if it is supposed to be a simple check at the beginning of a script, of which you only want a single instance running at a time, this is clean and works. What would be better (sincere question)?
Kevin Little
What Kevin wrote is in this case a cause. But can you explain, why wc (as well as awk field count) returns n+1? if I `echo $(pidof -x test.sh) > wc.txt` and then `wc -w wc.txt` it returns correctly `1 wc.txt`
Andrew
@Kevin: A lockfile. See Chris Johnsen's comment on my answer - this is the really easy case where they're all the same script, but in general as long as you can edit all the code, you can come up with a setup of lock(s) to get the behavior you want!
Jefromi
A: 

If you use the -o option to omit the PID of the script ($$), then only the PID of the subshell and any other instances of the script (and any subshells they might spawn) will be considered, so the test will pass when there's only one instance:

if [ $(pidof -x -o $$ test.sh | wc -w) -eq 1 ]; then echo Passed; fi
Dennis Williamson
+1  A: 

you don't have to pass the result of pidof to wc to count how many there are..use the shell

r=$(pidof -x -o $$ test.sh)
set -- $r
if [ "${#@}" -eq 1 ];then
 echo "passed"
else
 echo "no"
fi
ghostdog74
A: 

Here's how I would do it:

if [ "`pgrep -c someprocess`" -gt "1" ]; then
  echo "More than one process running"
else
  echo "Multiple processes not running"
fi
musashiXXX