At one point, you were close to what you seem to want.
a.sh
#!/bin/sh
echo "a.sh running"
sleep 10s
(sleep 10s; pkill -f b.sh) &
bash b.sh
sleep 10s
pkill -f b.sh
echo "a.sh still running"
b.sh
(same as your version)
echo "b is now running"
sleep 1000s
echo "Bananas"
The (sleep 10s; pkill -f b.sh) &
line in a.sh
creates a subshell that sleeps for ten seconds and then kills b.sh
.
It then puts that subshell into the background,
so a.sh
can keep running.
a.sh
then runs b.sh
, which runs sleep 1000s
.
Ten seconds later, the sleep 10s
in the subshell finishes,
and it (the subshell) runs pkill -f b.sh
, killing the b.sh
process.
a.sh
then resumes running.
The second pkill -f b.sh
does nothing,
since the b.sh
process is already terminated.
This is what happens when it runs:
$ ./a.sh
a.sh running # immediately
b is now running # about 10 seconds after the above
./a.sh: line 6: 13684 Terminated bash b.sh # about 10 seconds after the above
a.sh still running # about 10 seconds after the above
$ # immediately after the above
This has the advantage that a.sh
can resume running immediately
if b.sh
finishes quickly.
Only if b.sh
runs for more than 10 seconds does the pkill
kick in.
In the other answer, a.sh
has to sit idle
while the sleep
before the pkill
takes up time,
even if b.sh
has already wrapped up.
Another user posted a comment (now deleted) saying
I get
./a.sh
a.sh running
b is now running
Terminated
not what you report. Any idea?
It was a dark and stormy night. Suddenly, a shot rang out!
—It Was a Dark and Stormy Night, Snoopy
by Charles M. Schulz
The argument to pgrep
and pkill
is a regular expression;
specifically, an Extended Regular Expression1.
Try running cat
and cut
simultaneously2.
Then do pgrep c.t
— it will list two PIDs,
and you can confirm with ps
that they are cat
and cut
.
If you had done exactly what I said,
you really should have gotten the same results I did.
But I’m willing to wager 42 Zorkmids that you did something different.
You either:
- Began
a.sh
with #!/bin/bash
as a she-bang (first line)
instead of #!/bin/sh
, or
- Ran
a.sh
with bash a.sh
instead of ./a.sh
.
Suddenly, a shot rang out!
b.sh
is a regular expression that matches b
followed by any character followed by sh
.
It matches itself (b.sh
),
and also things like b!sh
, b~sh
, b&sh
, b7sh
, and bush
.
And … … (wait for it) … … it also matches bash
!
So pkill -f b.sh
kills all bash processes,
and also any others that have b.sh
in their names.
When I ran what I ran, my login shell
(which is at least partially immune to pkill
) was bash,
but a.sh
was running under /bin/sh
(but b.sh
was running under bash
).
The pkill -f b.sh
command killed the bash b.sh
process
because it contained both bash
and b.sh
.
But the /bin/sh
that was running a.sh
was unaffected.
Assuming you ran it the way I believe you ran it
(either of the two alternatives),
all the script processes were running bash
.
And so the pkill -f b.sh
command killed all the script processes.
Change pkill -f b.sh
to pkill -f 'b\.sh'
(escaping the dot so it matches only a dot, and not any character),
and you should get the right results.
________
1 pgrep(1)
2 If you have two terminals (shell windows),
run cat | cut -f1
(with input from the terminal) in one of them,
and the p
-commands in the other.
If you have only one terminal,
run sleep 42 | cat | cut -f1 &
(without the sleep
, the pipeline would collapse immediately),
and then run p
-commands in the same window.
pkill -f b.sh
within thea.sh
script?b.sh
has already finished before thepkill
line runs. I don't really think either of those outcomes is wrong, but it does mean that what works is going to depend on which platform you're targeting. That said, I don't think I understand what you're trying to do or trying to test and I suspect that your test script doesn't really represent what you want.