1. Overview
A process ID (PID) is a unique identifier assigned to a running process on a system. Retrieving the PID of a just-started process can be useful for monitoring and controlling the behavior of that process. For example, the PID can be used for termination at a later stage or to avoid running concurrent processes.
In this tutorial, we’ll explore various methods to obtain the PID of a process that we just initiated on a system.
In the examples below, we first start a sleep process and send it to the background using &. We also use the acquired PID to terminate a process using the kill command. In all cases, we may inspect background jobs using the jobs command.
In fact, one way to get the PID of a process is the output of backgrounding with &, which we use as further verification. Let’s explore others.
2. Get PID by Process Name
Every process has a unique process identifier or PID. In Linux, there are several ways to get the PID of a newly started process. Some of the most common methods are pidof or pgrep which are based on searching for the process name among running processes.
2.1. Using pidof
We can use pidof to find the PID of a process based on the process name:
$ sleep 30 &
[1] 3904
$ pidof sleep
3904
Here, pidof returns 3904 as the PID of our process.
We may then kill that process:
$ kill 3904
Alternatively, we may combine both commands on one line:
$ sleep 30 &
$ kill $(pidof sleep)
$ jobs
[1]+ Terminated sleep 30
Using jobs, we see that our sleep process has been terminated.
2.2. Using pgrep
Like pidof, the pgrep command can also search for the PID of a process based on the process name.
Let’s get the PID of the sleep process with pgrep:
$ sleep 30 &
[1] 3925
$ pgrep sleep
3925
We can monitor or kill the process based on this PID, which is 3925.
2.3. Using ps
The ps command can show all processes running on the system:
$ sleep 30 &
[1] 3949
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
... ... ... ... ... ... ... ... ... ... ...
sysadmin 3949 0.0 0.0 5260 744 pts/0 S 13:47 0:00 sleep 30
... ... ... ... ... ... ... ... ... ... ...
We can pipe the ps command into grep and awk to extract just the PID value:
$ sleep 30 &
[1] 3978
$ pid=$(ps aux | grep sleep | grep -v 'grep' | awk '{print $2}')
$ echo "$pid"
3978
Here, we use the first grep to match the pattern sleep, while grep -v ‘grep’ excludes the grep line from the output of ps. Finally, awk prints the second field, which in this case is the PID value.
3. Get PID Independent of Process Name
All the methods described so far have the same drawback: selecting a process to kill based on its name can be dangerous if the process is running in a production environment.
This is because there may be multiple processes running with the same name but with different PIDs. In such cases, we may not be sure that we’re selecting the correct process:
$ sleep 60 &
[1] 4011
$ sleep 30 &
[2] 4013
$ sleep 30 &
[3] 4014
$ pgrep sleep
4011
4013
4014
Here, we’re not able to select only one sleep process by name because there are multiple concurrent processes with the same name, some with the same argument.
The problem with obtaining a PID by process name via pidof, pgrep, or ps is that we’re relying on volatile information. While this may work in an interactive setting, it becomes dangerous if we automate the selection and killing process, e.g., via a script.
The same reasoning applies to commands such as pkill and killall when terminating a process directly based on its name.
3.1. Get PID via exec
One approach to get the PID of a process independently of its name is by spawning a shell and using exec to replace the current one with the process itself. The PID of the new process will be the same as the PID of the shell:
$ bash -c 'echo $$ > /tmp/test.pid && exec sleep 30' &
[1] 4026
$ cat /tmp/test.pid
4026
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
... ... ... ... ... ... ... ... ... ... ...
sysadmin 4026 0.0 0.0 5260 744 pts/0 S 09:09 0:00 sleep 30
... ... ... ... ... ... ... ... ... ... ...
The $$ variable expands to the current shell’s PID. Therefore, we can see that the sleep command takes on the PID of the spawned shell. This way, we’re setting the PID of the process before it even runs.
3.2. Get PID via $!
The built-in variable $! expands to the process ID of the most recently backgrounded process. We can use this to get the PID of a process right after sending it to the background. This way, we ensure that the PID belongs to the process we’re interested in:
$ sleep 30 &
[1] 4037
$ pid=$!
$ kill "$pid"
Here, we’ve saved the PID in a variable. Another option is to save it in a file:
$ sleep 30 &
[1] 4047
$ echo $! > /tmp/test.pid
$ kill `cat /tmp/test.pid`
Here, the PID value is in a temporary file named test.pid under the /tmp directory.
4. Avoiding Concurrent Processes: A Use Case
By obtaining the PID of a process correctly, we can kill the process at a later stage if we need to. Another important application is avoiding concurrent script execution, which is especially useful if the script implements backups.
Let’s see an example of how to avoid concurrent executions of the same script:
#!/usr/bin/env bash
FILE_PID="/tmp/test.pid"
if [ -f "${FILE_PID}" ]; then
echo "A concurrent script is already running: PID `cat ${FILE_PID}`"
echo "If not, please delete ${FILE_PID}"
exit 1
fi
echo $$ > "${FILE_PID}"
trap "rm ${FILE_PID}" EXIT
sleep 30
Here, script.sh checks for the existence of a file containing the PID of the script. If it finds the file, the script echoes that a concurrent process is already running and returns its PID before exiting with an error value of 1. Otherwise, the script saves its PID into the PID file. Also, a trap is set to delete the file when the script exits.
If we run script.sh in the background, we can check that the PID file /tmp/test.pid will exist for 30 seconds only, that is until the script exits:
$ bash script.sh &
[1] 4130
$ cat /tmp/test.pid
4130
Once sleep 30 completes and the script exits, the trap command triggers and deletes the file /tmp/test.pid.
If we try running the script twice concurrently, say in two separate shells, the second instance of the script will exit with an error after echoing that a concurrent process is already running.
5. Conclusion
In this article, we discussed several methods to get the PID of a newly started process. By using one of these methods, we can monitor and control the behavior of a running process.
Tools such as pidof, pgrep, and ps cannot ensure that the process will match the one we wish to select when based on the process name. By backgrounding a process and using $! to obtain its PID, we avoid relying on non-unique information such as process names.