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:
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:
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.
In this article, we walked through using a .pid file to track the PID of a process.