1. Overview

Sometimes it’s necessary to save the process identification number (PID) of a Linux process. In this tutorial, we’ll present a common way to store the PID using a .pid file and an example of how you would use it.

2. What Is a .pid File?

Sometimes an application will write the PID to a file for easy access. It is simply a text file that contains only the PID of a process. There’s no specific rule around creation or use. It’s just a helpful convention.

Let’s start by walking through a short example of creating a .pid file.

3. Creating a .pid File

Now let’s discuss the creation of a .pid file.

3.1. Initial File Creation

One way we can create a .pid file in a script is by piping the output of $$ to a file:

% echo $$ > myShell.pid
% cat myShell.pid
40276

$$ is a Linux variable that returns the PID of the process from which it is called. In this case, it’s the PID of the shell.

Now, let’s start with a small script:

#!/bin/bash
  
# create file
pid_file="process.pid"
echo $$ > $pid_file

count=0
while [ $count -le 10 ]
do
  echo Going $count..
  sleep 1
  count=$(($count+1))
done

When we run the script:

% ./process.sh

We’ll see two things. First, we will see the output of the process:

% ./process.sh
Going 0..
Going 1..
Going 2..
Going 3..
Going 4..
Going 5..
Going 6..
Going 7..
Going 8..
Going 9..
Going 10..

If we run ls in another terminal window, we’ll see the process.pid file:

% ls
process.pid    process.sh

It contains the PID of the process running the script. We can verify this by using cat on the file:

% cat process.pid
34876

3.2. .pid File Location

While we can put the .pid file anywhere, typically, we would have our process put the files in /var/run. In order to avoid clashes with other processes, we could go a step further and create a new directory, /var/run/myScript:

% echo $$ > /var/run/myScript/myShell.pid

On some systems, however, this directory may be owned by root, in which case it may not be possible for us to write our .pid file there. A second option would be the home directory:

% $$ > ~/myScript/myShell.pid

4. Killing a Process Using a .pid File

Now that we have a .pid file for our process, let’s think about what we can do with it.

Let’s say we want to kill a process while it’s running. If there’s a .pid file, we can get the PID from the file and then use it with xargs and kill. This ensures that we only need to know the name and location of the .pid file and not the actual PID itself:

% cat process.pid | xargs kill

The main benefit here is that we’re killing exactly the process we want to kill. We could do something like:

ps -ef | grep process

But, doing so could turn up multiple results if there are multiple instances of the app running. It would also turn up the process actually running the grep, for example:

% ps -ef | grep process
  501 40311 40276   0  7:00AM ttys001    0:00.00 grep process

In this case, we would have to account for unexpected matches before killing anything.

5. Ensuring a Single Instance of an Application

We can also use a .pid file to make sure an application isn’t already running before we start it. To do this, our script would need two changes. First, we need to remove the .pid file at the end of our run:

# clean up file after we're done
rm $pid_file

Second, we need to add a check at the beginning for the existence of a .pid file:

if [ ! -f $pid_file ]; then
  echo "Creating .pid file $pid_file"
  echo $$ > $pid_file
else
  echo "Found .pid file named $pid_file. Instance of application already exists. Exiting."
  exit
fi

If the file exists, we’ll assume the application is running and exit without running the rest of the script.

So now, our script looks like:

#!/bin/bash
  
# create file
pid_file="process.pid"

if [ -f $pid_file ]; then
  echo "Found existing .pid file named $pid_file. Exiting."
  exit
else
  echo "Creating .pid file $pid_file"
  echo $$ > $pid_file
fi

count=0
while [ $count -le 10 ]
do
  echo Going $count..
  sleep 1
  count=$(($count+1))
done

# clean up file after we're done
rm $pid_file

To see this in action, let’s open up two terminal windows and run our script in both of them:

% ./process.sh

The first window will run as expected:

% ./process.sh 
Creating .pid file process.pid
Going 0..
Going 1..
Going 2..

And the second window will detect the .pid file and exit without running:

% ./process.sh 
Found existing .pid file named process.pid. Exiting.

6. Handling a Stale .pid File

One issue we may run into with this implementation is a stale .pid file. Let’s say the application died without reaching the line to clean up our .pid file. If we restart the application, the file would still exist, and the script would exit without running.

We can extend our startup check to handle this situation. We can ensure that if the .pid file exists, the PID inside is also a valid process. Let’s try using pgrep on the contents of the .pid file:

pgrep -F process.pid

This will return a 0 exit code if a process matches the PID and a 1 if there is no process matching the PID.

So now, our script will have two checks at the start:

#!/bin/bash
  
# create file
pid_file="process.pid"

if [ -f $pid_file ]; then
  echo "Found existing .pid file named $pid_file. Checking."

  # check the pid to see if the process exists
  pgrep -F $pid_file
  pid_is_stale=$?
  old_pid=$( cat $pid_file )
  echo "pgrep check on existing .pid file returned exit status: $pid_is_stale"

  if [ $pid_is_stale -eq 1 ]; then
    echo "PID $old_pid is stale. Removing file and continuing."
    rm $pid_file
  else 
    echo "PID $old_pid is still running or pgrep check errored. Exiting."
    exit
  fi
else 
  echo "Creating .pid file $pid_file"
  echo $$ > $pid_file
fi

count=0
while [ $count -le 10 ]
do
  echo Going $count..
  sleep 1
  count=$(($count+1))
done

# clean up file after we're done
rm $pid_file

To see the “stale file” logic work, start the application at the command line and immediately kill it with CTRL+c. This will produce and leave a stale .pid file. Starting the script again, we’ll see:

./process.sh
Found existing .pid file named process.pid. Checking.
pgrep check on existing .pid file returned exit status: 1
PID 35975 is stale. Removing file and continuing.
Going 0..
Going 1..

We can see the “still running” logic by starting the application in one window and then starting it right away in another window. We’ll see this in the second window:

% ./process.sh
Found existing .pid file named process.pid. Checking.
35926
pgrep check on existing .pid file returned exit status: 0
PID 35926 is still running or pgrep check errored. Exiting.

So now, we have a script that uses a .pid file to ensure that we’re only running one instance of the application at a time.

7. Conclusion

In this article, we walked through using a .pid file to track the PID of a process.