25

There are several ways to do this, but I'm not sure which one of them is the best.

Here's what I can think of:

  • Look for the process using pgrep.
  • Have the script lock itself using flock, and then check if it is locked each time it runs.
  • Create a pid file in /var/run/program_name.pid and check for existence, and compare pids if needed.

There are probably more ways to do this. What do you think is the best approach?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Tom Feiner
  • 20,656
  • 20
  • 48
  • 51
  • Similar question, about Bash related: http://stackoverflow.com/questions/455911/whats-the-best-way-to-make-sure-only-one-instance-of-a-perl-program-is-running – codeforester Jan 18 '17 at 19:50

3 Answers3

42

There are many ways to do it. PID files are the traditional way to do it. You could also hold a lock on a file, for example the program itself. This small piece of code will do the trick:

use Fcntl ':flock';
open my $self, '<', $0 or die "Couldn't open self: $!";
flock $self, LOCK_EX | LOCK_NB or die "This script is already running";

One advantage over PID files is that files automatically get unlocked when the program exits. It's much easier to implement in a reliable way.

Leon Timmermans
  • 30,029
  • 2
  • 61
  • 110
  • doesn't seem to work well with ActiveState Perl... someone else has tried it? – golimar Oct 01 '12 at 09:43
  • +1 Nice solution, although I would add some brackets to make the code a little more clear. Is that 'or' statement in the second line working on only the $0 or the result of the open statement? Just doesn't look clear to me. – MikeKulls Aug 16 '14 at 03:31
  • 2
    @MikeKulls The [precedence table](http://perldoc.perl.org/perlop.html#Operator-Precedence-and-Associativity) in perlop may be helpful. It helps a lot to know that `or` is the lowest precedence operator in the language. – Leon Timmermans Aug 19 '14 at 17:53
  • @LeonTimmermans. That is not the point. IMO, the code is unclear and could be a lot clearer with some brackets. Especially that third line which has both a | and 'or'. Which one of them takes precedence? I could probably take a guess or look it up or test it but it's one of those things that I actually avoid trying to know and just add in some brackets to clear it up. It makes things more language independent as I can think of at least one language where that last line would be a problem. – MikeKulls Aug 25 '14 at 04:57
  • 3
    @MikeKulls: Sorry, but that is a matter of style/personal preference. IMO precedence is not something you want to "avoid trying to know", if you really want that try lisp. To me, adding superfluous parentheses only adds to the mental parsing effort. – Leon Timmermans Sep 23 '14 at 22:36
  • @LeonTimmermans In some cases I agree but only when the precedence is fairly simple. For someone who uses Perl a lot maybe your above code would make sense, maybe you struggle to see the issue. But for someone who uses a few languages it just looks plain wrong. I mean surely when you have | and 'or' in the one line some brackets make sense. Surely. – MikeKulls Sep 24 '14 at 06:37
  • 1
    BTW, with regards to "avoid trying to know", when I started programming I used to learn every last tiny detail of the language I was using. I would write code that used these little quirks to maybe save a few characters. These days I can't be bothered and instead code so if those idiosyncrasies of the language went one way or the other then my code would still work. Perl has more idiosyncrasies and shortcuts than any other language I've used and I find that code written that uses all of those is very difficult to understand if you aren't a Perl expert. – MikeKulls Sep 24 '14 at 07:00
  • 2
    «I mean surely when you have | and 'or' in the one line some brackets make sense». Binary-or has a tighter precedence than logical-or in every C derived language (and others); this is not a Perl idiosyncrasy. «$0 or» is a Perl idiosyncrasy, but you have to realize the whole point of having low precedence logical operators (in addition to the high precedence || and &&) is that you can avoid parentheses. – Leon Timmermans Sep 24 '14 at 19:29
  • 1
    Right, but in any other language I've used the comma in the function call would take precedence. Not using Perl a huge amount that 3rd line (and even the second line) looks VERY unclear to me. Basically you are programming for yourself where I am programming for a maintenance programmer. Your choice I guess. – MikeKulls Sep 26 '14 at 05:32
10

Do the old PID file trick.

  • start process
  • see if there is a file called "myprog.PID"
  • check for existence of running proc. with matching PID using kill 0, $pid
  • if prog name of PID proc. matches, complain loudly and exit
  • if not, clean up stale "myprog.PID"
  • create a file called "myprog.PID" and then continue

HTH

cheers,

Rob

Rob Wells
  • 36,220
  • 13
  • 81
  • 146
  • 6
    This can be improved by reading the PID from the file and checking to see whether the process still exists (using kill 0, $pid). If not, then ignore the old pid file and proceed. This helps recover more gracefully from a crashed process. – Greg Hewgill Jan 18 '09 at 21:33
  • +1 for checking whether the pid found in the file exists, kill with signal 0 exists just for that. – Keltia Jan 18 '09 at 21:50
  • 3
    Note that this also has a race condition if two processes try to start at the same time, check for the file at the same, and neither file a pid file. – brian d foy Jan 19 '09 at 20:05
  • This method doesnt work if myprog.pl crashes. PID numbers are reused after some time so even if the process exists now, it could be some different program or daemon running. If myprog.pl is somehow guaranteed to have ended within say one minute, and the myprog.$$ file is older than that, then you could assume its another process and proceed anyway. ($$ is of course the PID number of your running myprog.pl). To check if it's another process you could on Linux (at least some Linux systems) parse `qx(ps aux|grep $$)` or `/proc/$$/cmdline`. I would go for the `flock` method above. – Kjetil S. Mar 24 '18 at 05:20
2

All of the options that you list are fine. One thing with this though, is to be aware that in rare cases, you can end up with a process that runs for a very long time (i.e., stuck waiting on something). You might want to think about keeping an eye on how long the other running instance has been running and possibly send yourself an alert if it exceeds a certain amount of time (such as a day perhaps).

Scott Hoffman
  • 547
  • 7
  • 21