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!

jww
  • 97,681
  • 90
  • 411
  • 885
Gargauth
  • 2,495
  • 6
  • 27
  • 30
  • 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. – Cascabel Mar 27 '10 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? – Cascabel Mar 27 '10 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. – Cascabel Mar 27 '10 at 17:26
  • @Jefromi: I don't have any more code (yet) than what I wrote in the question. – Gargauth Mar 27 '10 at 17:30

7 Answers7

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..."
Kevin Little
  • 12,436
  • 5
  • 39
  • 47
  • 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. – Cascabel Mar 27 '10 at 17:36
  • 1
    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 Mar 27 '10 at 17:41
  • 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` – Gargauth Mar 27 '10 at 17:52
  • 1
    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! – Cascabel Mar 27 '10 at 18:02
  • `-gt` means greater than. So `-gt 2` = `more than 2` (3, 4, 5, 6 ... n), NOT `more than 1` – MaXi32 Sep 17 '20 at 01:50
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.

Cascabel
  • 479,068
  • 72
  • 370
  • 318
  • 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. – Gargauth Mar 27 '10 at 17:46
  • @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. – Cascabel Mar 27 '10 at 17:48
  • 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 Mar 27 '10 at 17:52
  • @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... – Gargauth Mar 27 '10 at 17:56
  • @Andrew: Yes, you're spawning a subshell when you use the pipeline, but not when you use the redirect. Read my answer again. – Cascabel Mar 27 '10 at 17:58
  • @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... – Gargauth Mar 27 '10 at 18:01
  • @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) – Cascabel Mar 27 '10 at 18:03
  • @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 Mar 28 '10 at 00:02
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
ghostdog74
  • 327,991
  • 56
  • 259
  • 343
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##*/}

Zrin
  • 919
  • 15
  • 25
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
Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
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
musashiXXX
  • 4,192
  • 4
  • 22
  • 24
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"
PePa
  • 61
  • 6