1
for i in $( seq 1 $1 )
do
        kill %$i
done

I try to kill the stopped jobs with this script, but interestingly it can't be able to even though I have jobs open.

$ jobs
[10]   Stopped                 vim detect_thread.py
[11]   Stopped                 python3 detect.py
[12]-  Stopped                 python3 detect.py
[13]+  Stopped                 python3 detect.py
$ kill 13
bash: kill: (13) - No such process
$ ./delete.sh 13
./delete.sh: line 8: kill: %1: no such job
./delete.sh: line 8: kill: %2: no such job
./delete.sh: line 8: kill: %3: no such job
./delete.sh: line 8: kill: %4: no such job
./delete.sh: line 8: kill: %5: no such job
./delete.sh: line 8: kill: %6: no such job
./delete.sh: line 8: kill: %7: no such job
./delete.sh: line 8: kill: %8: no such job
./delete.sh: line 8: kill: %9: no such job
./delete.sh: line 8: kill: %10: no such job
./delete.sh: line 8: kill: %11: no such job
./delete.sh: line 8: kill: %12: no such job
./delete.sh: line 8: kill: %13: no such job
1

2 Answers 2

2

The kill builtin only recognizes the %N format for jobs running in the current shell. However, shell scripts run in their own separate subshell and in that subshell, there are no jobs to kill. This might be clearer with an example:

$ for i in {1..5}; do sleep 100 & done
[1] 2259152
[2] 2259153
[3] 2259154
[4] 2259155
[5] 2259156
$ for i in {1..5}; do kill %$i; done
[1]   Terminated              sleep 100
[2]   Terminated              sleep 100
[3]   Terminated              sleep 100
[4]-  Terminated              sleep 100
[5]+  Terminated              sleep 100

As you can see, that works as expected if you run both sets of commands in the same shell session. Similarly, it also works if you launch and kill the commands from the same shell script:

#! /usr/bin/env bash
for i in {1..5}; do
  sleep 100 &
done

## Show the running jobs
runningSleepJobs=$(pgrep -c sleep)
echo "There are $runningSleepJobs sleep jobs running!"

for i in {1..5}; do
  kill %$i;
done
## Show that they've been stopped
runningSleepJobs=$(pgrep -c sleep)
echo "Now there are $runningSleepJobs sleep jobs running!"

If I now run this script, I can see it both starts and then kills the jobs as expected:

$ foo.sh
There are 5 sleep jobs running!
Now there are 0 sleep jobs running!

There is a way around this, however. Instead of executing your script, you can source it so that it runs in the current shell:

$ cat ~/bin/foo.sh
#! /usr/bin/env bash
for i in $( seq 1 $1 )
do
        kill %$i
done

$ for i in {1..5}; do sleep 100 & done
[1] 2295221
[2] 2295222
[3] 2295223
[4] 2295224
[5] 2295225

$ jobs
[1]   Running                 sleep 100 &
[2]   Running                 sleep 100 &
[3]   Running                 sleep 100 &
[4]-  Running                 sleep 100 &
[5]+  Running                 sleep 100 &

$ . ~/scripts/foo.sh 5
[1]   Terminated              sleep 100
[2]   Terminated              sleep 100
[3]   Terminated              sleep 100
[4]-  Terminated              sleep 100
[5]+  Terminated              sleep 100
1

A shell script will run with its own environment, which means the job management will not know about the job roster of the shell from which the script is executed. Use . to run the script in your current environment:

$ . ./delete.sh 13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.