Bash / Linux: check if a process or script is already running (PID, pidof, flock)

Linux and Bash ways to check if a process is running, if a PID still exists, and how to stop a second copy of the same shell script—using kill -0, pidof, pgrep, and flock with sane lock files and cleanup.

Published

Updated

Read time 4 min read

Reviewed byDeepak Prasad

Bash / Linux: check if a process or script is already running (PID, pidof, flock)

If you run backups, report jobs, or pollers from cron or systemd, you eventually need two different checks: whether some PID is still alive on the system, and whether your own script is already running so a second start does not pile on top of the first. The sections below cover both, with patterns I use on real servers.

Tested all commands and short examples from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.


Bash: check if a PID is still running (kill -0)

On Linux the usual test is kill -0: it sends no signal, but the kernel still checks whether you may signal that process. If the PID does not exist, the shell prints an error and returns non-zero.

bash
pid=12345
if kill -0 "$pid" 2>/dev/null; then
  echo "PID $pid is running"
else
  echo "PID $pid is not running (or not visible to this user)"
fi

Example when the PID is invalid:

text
bash: line 1: kill: (99999999) - No such process

You can pair this with /proc/$pid on Linux ([[ -d /proc/$pid ]]): if the directory is missing, the process is gone. That avoids parsing kill’s stderr when you only need a yes/no.


Linux: check if a process is running by name (pidof, pgrep)

When you care about a program name rather than a PID you already saved, pidof prints matching PIDs (GNU/Linux). That covers the usual linux check if program is running style question when the binary name is stable (systemd, sshd, your own compiled service).

bash
pids=$(pidof systemd)
if [[ -n "$pids" ]]; then
  echo "systemd is running with PID(s): $pids"
else
  echo "no systemd processes found (unusual on a normal Linux box)"
fi

On the machine used for testing, that printed:

text
systemd is running with PID(s): 4970

pgrep matches the command line, which is flexible but easier to get wrong: a pattern that is too short can match unrelated processes. Prefer an explicit pattern and read man pgrep for -f, -x, and -u before using this in production.


For bash check if script is already running, a file lock with flock is the approach I reach for first. The kernel serializes lock holders; you do not have to guess how many pgrep matches are “really” your script, and you do not leave stale PID files behind when a node reboots mid-run.

Put this near the top of the script (after set options you want), pointing LOCK at a path your user can write (often under /var/tmp or a dedicated app directory):

bash
#!/usr/bin/env bash
set -euo pipefail

LOCK=/var/tmp/my-backup-job.lock
exec 9>"$LOCK"
if ! flock -n 9; then
  echo "Another instance is already running; exiting."
  exit 1
fi

# ... rest of script ...

flock -n is non-blocking: if another instance holds the lock, you exit immediately. When the script ends, the lock is released automatically when the file descriptor closes.


PID files: when they help, and how to avoid stale locks

Some daemons still use a PID file under /run or /var/run. That pattern answers bash check if process exists once you have written the PID—but the file is a cache, not the truth. If the script dies without unlinking the file, the next start must detect a stale PID (process gone) and replace the file.

A minimal pattern:

  1. If the PID file exists, read the PID and run kill -0 (or check /proc/$pid).
  2. If that PID is dead, remove the PID file and continue.
  3. After you know you are the sole runner, write your $$ (or the long-lived child’s PID) to the file.
  4. Use a trap so EXIT and common signals remove or update the file; see capture Ctrl+C in bash for cleanup patterns.

Never pgrep your script’s basename, write every match into a PID file, and delete that file at the end of the same run—the older snippets that did that were easy to misread and could fight concurrent legitimate runs.


If your goal is the opposite—run several workers at once and collect exit status—use the patterns in run shell scripts in parallel and collect exit status instead of a single-instance lock.


Summary

Use kill -0 (or /proc/$pid) when you already have a PID and need to know if that Linux process is still running. Use pidof or a carefully chosen pgrep when you are matching by program name. For bash check if script is already running, prefer flock on a dedicated lock file so only one instance executes and you do not depend on fragile basename matching. If you must use a PID file, treat it as a hint: verify the PID is alive, clean up on exit with trap, and delete stale files when kill -0 fails.

Deepak Prasad

R&D Engineer

Founder of GoLinuxCloud with more than 15 years of expertise in Linux, Python, Go, Laravel, DevOps, Kubernetes, Git, Shell scripting, OpenShift, AWS, Networking, and Security. With extensive …