1. Overview

There are situations where we may want to kill processes that have been running for a long time. For instance, we may expect a process to finish its task within a certain time. However, if the process takes longer than expected, it may be because an error has occurred. In this tutorial, we’ll learn how to kill the processes that are older than a certain time. To do this, we’ll learn three alternatives. One involves using the killall command, another using the pkill command, and the last using the ps command.

2. Using killall

One simple way of killing processes older than a certain age is to use the killall command. This command takes the name of the process we want to kill as a parameter. To kill only the processes older than a certain age, we must add the -o parameter and specify the desired age. The -o parameter takes as an argument the amount of time, which can be in different units. The time unit can be s, m, h, d, w, M, or y, meaning seconds, minutes, hours, days, weeks, months, and years. Let’s kill all processes named ping older than 12 hours:

$ killall -o 12h ping

When we run the command above*, killall* sends the SIGTERM signal to all ping commands running for more than 12 hours. If we think the process won’t die with the SIGTERM signal, we can terminate the processes using other signals. For example, this can happen if we believe the process is hung and doesn’t respond to commands. So, we can change the signal using the parameter -SIGNAL. For instance, to use SIGKILL, we have to use the parameter -SIGKILL. Let’s kill all processes named netcat older than one day, using the SIGKILL signal:

$ killall -o 1d -SIGKILL netcat

So far, we have learned how to kill processes with a specific name. However, killall can interpret the name as a regular expression if we add the -r parameter. This allows us to kill processes with different names. Let’s suppose we have several bash scripts running, and we want to kill only those running for more than 2 hours. Then, if all those bash scripts are files ended with .sh, we can use the \.sh$ regular expression. Let’s try it:

$ killall -o 2h -r '\.sh$'

This way, we kill only the processes ending with .sh and older than 2 hours.

3. Using pkill

We can also use pkill to terminate processes running for a long time. The pkill command is similar to the killall command, but the name filter is always a regular expression. As the pkill command behaves the same way as pgrep, we can use pgrep with the same parameters we would use with pkill. This way, we can check which processes will be killed before we actually do it. When we use pkill to kill processes, we can add the -O parameter followed by the age in seconds. By doing this, we kill only the processes older than that age. Let’s kill all processes named ping older than 43200 seconds (12 hours):

$ pkill -x -O 43200 ping

Notice we used the -x parameter. This parameter tells pkill to kill all processes matching exactly the name “ping“. So, that example kills all processes named ping, without killing other processes with a similar name, like arping or ping6. Similar to killall, pkill sends the SIGTERM signal by default. However, we can change which signal pkill sends using the parameter -SIGNAL. Let’s repeat the previous example this time using the SIGKILL signal:

$ pkill -SIGKILL -x -O 43200 ping

Finally, we can use regular expressions too. In this case, we don’t have to add any extra parameters as we did with killall. Let’s kill all processes older than 3600 seconds (1 hour) ending with .sh:

$ pkill -O 3600 '\.sh$'

Notice this time we don’t use the -x parameter because in that case, pkill would match the exact pattern “.sh” instead of including the names ending with “.sh”.

4. Using ps

Finally, we can also use the ps command combined with the awk and kill commands to kill processes older than a certain age. In this case, we’ll first list the processes with ps and filter them according to their age and name using awk. And finally, we’ll use kill to terminate them.

4.1. Listing Processes

With the ps command, we can choose which columns we want to see. First, we use the -e parameter to select all processes. Then we add the -o parameter followed by the desired columns. We can use the etimes column to get the time in seconds since the process started. Let’s try it choosing the columns pid, etimes, and cmd, so we see the PID, the age in seconds, and the command of each process:

$ ps -eo pid,etimes,cmd
  PID ELAPSED CMD
    1  148168 init [3]
...
 1703  148869 sshd: /usr/sbin/sshd 
 5223    4751 screen
 5239    4750 -/bin/bash
 6135    1942 cmus
23817   94840 cmus
25405  120136 /usr/lib64/firefox/firefox-bin
27613       0 ps -eo pid,etimes,cmd
29739  119782 cmus
...

This is also useful when we just want to see how much time some processes have been running without killing them. Now, we can pipe the ps output to awk to filter the processes older than a certain age. We have to compare the second column and see if it is greater than the desired age. Let’s use awk to list only the processes older than 7200 seconds (2 hours):

$ ps -eo pid,etimes,cmd | awk '$2 > 7200'
    1  148168 init [3]
...
 1703  148880 sshd: /usr/sbin/sshd 
23817   94851 cmus
25405  120147 /usr/lib64/firefox/firefox-bin
29739  119793 cmus
...

As we see, we listed all the processes running for more than 2 hours. However, we don’t want to kill all of them. For instance, we shouldn’t kill processes like init or systemd. So, we can add more filters to awk to select only the processes we want to kill. We can do this by comparing the third column. Let’s select all processes named “cmus” older than 7200 seconds:

$ ps -eo pid,etimes,cmd | awk '$2 > 7200 && $3 == "cmus"'
23817   94879 cmus
29739  119710 cmus

Finally, we can write a function called filter_processes_older_than and receive the age and name by parameters. Let’s do it:

$ filter_processes_older_than() {
    ps -eo pid,etimes,cmd | awk '$2 > '$1' && $3 == "'$2'"'
}
$ filter_processes_older_than 7200 cmus
23817   94883 cmus
29739  119724 cmus

4.2. Killing the Processes

Now that we have the list of processes we want to kill, we can use xargs with kill. First of all, we have to get the PID of each process. So, we can write a new function called filter_pids_older_than to print only the PID of each process. This way, we can keep our filter_processes_older_than function intact to verify which processes would be killed. Let’s write the filter_pids_older_than function to print only the PID, which is in the first column:

$ filter_pids_older_than() {
    filter_processes_older_than "$1" "$2" | awk '{print $1}'
}
$ filter_pids_older_than 7200 cmus
23817
29739

Now, to kill those processes, we pipe that output to xargs kill:

$ filter_pids_older_than 7200 cmus | xargs kill

This way, we kill all the cmus processes running for more than 7200 seconds. We can change the signal we use to kill the processes by adding the -SIGNAL parameter to kill. So let’s write a new function called kill_pids_older_than, with a third optional parameter to specify the signal:

$ kill_pids_older_than() {
    filter_pids_older_than "$1" "$2" | xargs kill -${3:-SIGTERM} 
}

We can notice we used ${3:-SIGTERM}. This will use the third parameter only if it is defined. If it is not defined, the default value SIGTERM is used. Finally, we can test this all together. Let’s first verify which processes would be killed with the filter_processes_older_than function, and then we kill them with the kill_pids_older_than function using the SIGKILL signal:

$ filter_processes_older_than 7200 cmus
23817   94955 cmus
29739  119796 cmus
$ kill_pids_older_than 7200 cmus SIGKILL

5. Conclusion

In this article, we saw how to kill processes that are older than a certain age. We learned we could use the killall command adding the -o parameter and the desired age. Then, we learned about pkill, which is similar to killall, but we have to use the -O parameter. And finally, we saw we could use the ps command to list all the processes older than a certain age and then kill them using awk, xargs, and kill.