11

Can I make a bash command line that only runs a certain command if the process is not already running (in the background)?

How do I check*, if a command is already running?

(so I can add the next command with && in between them so the next one only executes if the first one is true).

*: test, determine, discover, find out

n611x007
  • 1,007

10 Answers10

6

Use daemontools. You can use svok to check whether a service/daemon/background process is currently running.

For other methods see:

Lesmana
  • 27,439
3

I've used this technique from time to time:

$ pgrep <process name> || <process name>

Prior to pgrep it used to be done this way:

$ ps -eaf | grep -q <[p]rocess name> || <process name>

example

The bit with the [p] makes it so that the grep won't find itself as a result.

$ ps -eaf | grep -q [s]leep || sleep 10
slm
  • 369,824
2

This can be tricky, because you can have separate instances of the same process that live independently. For example servers listening on different ports, or services running as different users. In order to distinguish between these instances, you need to assign each of them a unique tag. The tag is often a file, but it can be a local socket in the abstract namespace, a TCP port, etc. — any unique identifier will do. When the tag is a file, it can be a regular file containing a process ID (a pidfile), or a named pipe or socket that the file is listening on, etc. Ideally, the tag is a communication endpoint that allows clients to connect to that process.

Each of these different kinds of tags results in a different way of checking whether the instance you are seeking is up and running. For example, with a local file socket, try to connect to it, and start the process if there is no process listening on that socket. If the tag is a pidfile, check if there is a process with that process ID, but beware that this is fragile, since if the process has died, there may be an unrelated process that has reused its ID. Beware that if two clients try to reach the process in a short time frame, they might both find that the process doesn't exist and both attempt to start it; properly protecting from this race condition can be tricky.

It is easier to manage instances when they are all started by the same supervisor process, and that supervisor process detects when instances die and reacts accordingly. Many service monitoring programs that can do this.

If the program doesn't respond on a known communication endpoint and it isn't managed by a supervisor program, the poor man's tag is a pidfile: a file containing the process ID. When you start the process, write the pid to a file with a prearranged name. When you need the process to exist, read the pidfile and see if there is a process with that pid. When you kill the process, erase the pidfile. The most salient problem with an unsupervised pidfile is that if the process dies, its pid may be reused by some unrelated process. You should at least check the process name or the process executable to ensure that you are talking to the right process. Many unix variants have a pgrep command: pgrep SOMENAME lists the processes whose name contains SOMENAME as a substring, with additional options to limit to a particular user, to require an exact match, to change which of the several possible notions of “process name” is used, etc.

1

I'm sorry but all these solutions does not support contab since the same command line will appear twice in the 'ps' result.

So here is mine :

## Test pour voir si le même script tourne déjà
## Un fichier .pid est utilisé pour stocké le numéro de process
## Si le pid est en train de tourner alors on sort.
lock_file=$0".pid"
[ -r $lock_file ] && read pid <$lock_file
if [ "$pid" -gt 1 ] && [ `ps --no-headers -p "$pid" | wc -l` -gt 0 ] ; then
    echo "WARNING : le process $pid tourne deja : $0"
    ps -edf | grep `basename $0` | grep -v grep
    echo "WARNING : Arrêt de cette instance ($$)."
    exit 7
fi
echo $$ >$lock_file
Francis
  • 11
1

You could use this approach:

if [[ -z $(ps -C appname -opid=) ]]; then
    appname && secondapp
fi
jasonwryan
  • 73,126
1

Other options:

  • pgrep -xq processname
    • Only matches the first 15 characters in GNU/Linux
    • Doesn't include ancestors on OS X
  • ps -eo comm= | sed 's|.*/||' | grep -xq processname
    • Only matches the first 15 characters in GNU/Linux
    • sed 's|.*/||' removes dirname parts on OS X

In GNU/Linux ps -o comm truncates command names to 15 characters and pgrep and ps -C only match the first 15 characters.

ps -C (match command names) is not supported on OS X.

In OS X ps -o comm prints the absolute paths of commands and ps -co comm only prints command names. In GNU ps -o comm only prints command names and -c has a different meaning.

OS X's pgrep doesn't include ancestor processes (like bash, Terminal, or launchd) without -a. GNU's pgrep includes them by default and it doesn't support -a.

grep -x and pgrep -x don't imply -F, so use -Fx if the process name can contain regex characters.

Lri
  • 5,223
0

Get the state of your process:

ps -lp $(pgrep <YOUR_PROCESS_NAME>) | tail -1 | awk '{print $11}'

Reference:

D    uninterruptible sleep (usually IO)
R    running or runnable (on run queue)
S    interruptible sleep (waiting for an event to complete)
T    stopped, either by a job control signal or because it is being traced
W    paging (not valid since the 2.6.xx kernel)
X    dead (should never be seen)
Z    defunct ("zombie") process, terminated but not reaped by its parent

For example, I've used it to conditionally play or pause my sox process on a tmux session:

/usr/local/bin/tmux if-shell -t sox "[ $(ps -lp $(pgrep sox) | tail -1 | awk '{print $11}') == 'T' ]" \
  'send -t sox "fg" Enter' \
  'send -t sox C-z'
0

With Bash

#!/usr/bin/env bash

[[ $# -eq 0 ]] && { echo -e "Usage:\t\t $0 <Process_name>  <Command here>"; exit 1;  }

ifnotrun(){
        local p=$1
        if ! ps -C "$1" -opid=
        then
                cmd=($@)
                echo ${cmd[@]:1}
        else
                echo "Process \"$p\" Already Running.."
                exit 1
        fi

}

ifnotrun $*

Note:- remove echo if output looks Ok.

Rahul Patil
  • 24,711
0

I will explain the case where you run command in beckgreoud. The "$!" save the PID of the last background proccess. So, you can use it to find it in the the proccess tables following the answers above:

sleep 4 &
ps -ef | grep -w  $!  ...

The builtin command "jobs" - try this:

sleep 4&
J=`jobs`
while [ "$J" ]; do
        sleep 1
        jobs # This line flush the jobs' bufer.
        J=`jobs`
done

Regarding the "&&" option

Instead of && use the command wait. In the following example myproc2 will no run until myproc1 would finish:

myproc1 &
wait
myproc2
Udi
  • 75
-1

You can use it inside a script:

if [ `ps -ef | grep "script.sh" | grep -v grep | wc -l` -gt 1 ] ; then
echo "RUNNING...."
else
echo "NOT RUNNING..."
fi
Nidal
  • 8,956
Pravs
  • 11