How to stop a bash while loop running in the background?

I started a while loop as follows:

while true; do {command}; sleep 180; done &

Notice the &.

I thought when I killed the terminal, this while loop would stop. But it is still going. It has been hours since I killed the terminal session.

How do I stop this while loop?


Solution 1:

I started

while true; do yad; sleep 60; done &

and closed the terminal to test it, now I got the same problem.

If you already closed the terminal you've started the loop in

Let's get an overview of running processes with ps fjx, the last lines on my machine read

 2226 11606 11606 19337 ?           -1 S     1000   0:00  \_ /bin/bash
11606 22485 11606 19337 ?           -1 S     1000   0:00  |   \_ sleep 10
 2226  9411  9411  8383 ?           -1 S     1000   0:00  \_ /bin/bash
 9411 17674  9411  8383 ?           -1 Sl    1000   0:00      \_ yad
    1  2215  2215  2215 ?           -1 Ss    1000   0:00 /lib/systemd/systemd --user
 2215  2216  2215  2215 ?           -1 S     1000   0:00  \_ (sd-pam)

You can see yad as a subprocess of /bin/bash, if I close yad it changes to sleep 60 in the output of ps. If the tree view is too confusing you can also search the output as follows1:

ps fjx | grep "[y]ad"  # or respectively
ps fjx | grep "[s]leep 60"

The output lines begin with two numbers, the first being the PPID, the process ID of the parent process and the second being the PID, the ID of the process itself. It's because of that 9411 appears in both rows here:

2226  9411  9411  8383 ?           -1 S     1000   0:00  \_ /bin/bash
9411 17674  9411  8383 ?           -1 Sl    1000   0:00      \_ yad

That's the bash subshell we want to kill and we just found out its PID, so now everything that remains is a simple

kill 9411  # replace 9411 with the PID you found out!

and the annoying subshell is gone for good.

1: The notation as grep "[y]ad" instead of simply grep yad prevents the grep process itself from showing up in the results list.


If you have the terminal still open

bash provides the variable $!, which “expands to the process ID of the job most recently placed into the background”, so the following just kills the latest process in the background:

kill $!

If it's not the latest process, just can get a list of running jobs with the jobs builtin, example output:

[1]-  Running                 while true; do
    yad; sleep 60;
done &
[2]+  Stopped                 sleep 10

There are two jobs in the job list, to kill one of them you can access it with the job number or the shortcuts %, %+ (“current job”) and %- (“previous job”), e.g. to kill the loop running in the background you could do

kill %1  # or
kill %-

and to kill the suspended sleep 10 job:

kill %2  # or
kill %+  # or
kill %

Solution 2:

I need to note that when you close the terminal where you entered that command, the sub-command should've died along with it. Trying to reproduce this showed this behavior.

Now, assuming that you indeed are in a situation where this didn't happen, one way to go about killing the program you left running in the background, which was caused by the & at the end of the command, is as follows:

  1. Open a new shell and run echo $$ to note your current PID (process ID), so that you don't kill your own session.
  2. Identify the PID of the program you think is still running; you can do this using the ps -ef | grep $SHELL command to find which programs are running a shell.
  3. Note the 2nd column from the left and note the numeric value that differs from your echo $$ result above; this is the PID you want to kill.
  4. Run the kill -SIGTERM <pid> command, where <pid> is the numeric value you identified in step #3
  5. Confirm the program is no longer running by repeating step #2

You could also restart your session or your computer, but the above will allow you to avoid that.