Skip to main content

Fixed: "pkill -f" Killed Another Script That Only Mentioned the Name

· 4 min read
Jagdish Kumawat
Founder @ Dewiride

I stopped my API with pkill -f, and a long script in another terminal died too, with exit code 143 and no error message. The script wasn't the API. It only had the API's name somewhere in its command line, and that's all -f needs.

What I Was Doing​

A long script was running in one terminal, and my API in another, on macOS. Here's the same thing in its smallest form. The API's stand-in, in terminal 1:

Terminal 1
python3 -c 'import time; time.sleep(30)' my-api-test

A script that mentions that name, in terminal 2:

Terminal 2
zsh -c 'echo "Testing my-api-test"; sleep 8; echo "All tests finished"'; echo "exit code: $?"

And in terminal 3, I stopped the API:

Terminal 3
pkill -f my-api-test

What I Expected​

The API to stop, and the script to finish.

What I Got​

Output (terminal 2)
Testing my-api-test
exit code: 143

The script printing Testing my-api-test and then exit code: 143, with All tests finished never printed.

All tests finished never printed.

What I Checked​

The exit code. 143 is 128 plus 15, and signal 15 is SIGTERM, which is what pkill sends by default. So pkill had stopped it.

What the pattern matches. pgrep takes the same options and only lists, so it shows what pkill would hit:

Terminal 3
pgrep -lf my-api-test
Output (without the process IDs, and with Python's path shortened)
python3 -c import time; time.sleep(30) my-api-test
zsh -c echo "Testing my-api-test"; sleep 8; echo "All tests finished"

There are two matches: the API, and the script.

The man page. man pkill on macOS says of -f: "Match against full argument lists. The default is to match against process names." And of -a: "Include process ancestors in the match list. By default, the current pgrep or pkill process and all of its ancestors are excluded".

That second sentence explains why this can look safe. A script that calls pkill -f on a name in its own command line doesn't kill itself on macOS. A script in another terminal gets no such protection.

Why It Happens​

Without -f, pkill compares the process's name. With it, the pattern is searched for anywhere in the whole command line, of every process you're allowed to signal. That includes shells, editors and tails that merely mention the text.

The Fix​

Stop the process you started, by its ID. Save it when you start the process:

Terminal 1
python3 -c 'import time; time.sleep(30)' my-api-test &
echo $! > api.pid

And use it to stop that process and nothing else:

Terminal 3
kill $(cat api.pid)

If you do need a pattern, run pgrep -lf with it first, and make it specific enough to match one line.

After the Fix​

Output (terminal 2)
Testing my-api-test
All tests finished
exit code: 0

The script printing Testing my-api-test, All tests finished and exit code: 0.

Common Mistakes​

MistakeWhat happensDo this instead
Using pkill -f with a short nameAnything mentioning it stopsUse the saved ID
Testing it from the script itselfIt's excluded as an ancestor, so it looks safeCheck with pgrep -lf from elsewhere
Using kill -9 firstThe process can't clean upSend the default signal

Troubleshooting​

SymptomLikely causeFix
A script dies with 143 and no messageSomething sent SIGTERMLook for a pkill or kill elsewhere
pkill without -f finds nothingThe name is dotnet or python, not yoursUse the saved ID
kill: no such processIt had already stoppedRemove the old ID file

FAQ​

Is this the same on Linux?​

I tested macOS's pkill. The -a rule about ancestors is in its man page, so read yours before relying on it.

Conclusion​

pkill -f matches any process with your text anywhere in its command line. Check with pgrep -lf, and for a process you started yourself, save its ID and kill that.

Additional Resources​

  • man pkill, on your own Mac — -f and -a.

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.