6

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!

4
  • It's good practice to always quote expressions in tests, in case they turn out to be the empty string. In this case, though, I'm pretty sure wc -w is guaranteed to print something? Better safe than sorry though. Commented Mar 27, 2010 at 17:23
  • Can you echo it outside of the if to check what it's printing? See if it's not working because it's zero, or because it's two? Commented Mar 27, 2010 at 17:25
  • @pajton: I expect it's something silly like spawning a subprocess of the same name - something that's done outside the code we can see. Commented Mar 27, 2010 at 17:26
  • @Jefromi: I don't have any more code (yet) than what I wrote in the question. Commented Mar 27, 2010 at 17:30

7 Answers 7

6

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..."
Sign up to request clarification or add additional context in comments.

5 Comments

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.
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)?
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
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!
-gt means greater than. So -gt 2 = more than 2 (3, 4, 5, 6 ... n), NOT more than 1
3

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.

8 Comments

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: er, wc -w definitely counts correctly. Just try "echo 1352 | wc -w" and you can easily see it counts one pid as one word.
Since all the actors can cooperate (they are instances of the same script), a lockfile seems like the way to go. I like lockfile linux.die.net/man/1/lockfile from the procmail package procmail.org
@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: Yes, you're spawning a subshell when you use the pipeline, but not when you use the redirect. Read my answer again.
|
2

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

Comments

1

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

Comments

1

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

Comments

1

If you don't want to use a lockfile ... you can try this:

#!/bin/bash

if [[ "$(ps -N -p $$ -o comm,pid)" =~ $'\n'"${0##*/}"[[:space:]] ]]; then
    echo "aready running!"
    exit 1
fi

PS: it might need adjustment for a weird ${0##*/}

Comments

0

Just check for the existence of any one (or more) process identified as test.sh, the return code will be 1 if none are found:

pidof -x test.sh >/dev/null && echo "Passed"

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.