1

Since there's a 15-character limit on the length of process names in pgrep, I've been using pgrep -f to give me a list of process id's of running commands with longer names. However, it includes the full command line parameters, and this gives wrong results.

For example: suppose my_long_script_name.sh is running while I vi my_long_script_name.sh (ignore editing a running program for the moment).

If I run pgrep -f my_long_script_name.sh, it returns the process id of both my_long_script_name.sh and vi my_long_script_name.sh.

How can I get a list of process id's (that I can feed to kill in a list, the same way pgrep -d ' ' makes them) that are based only on process names not parameter lists?

I'm running Buster but see the same problem in earlier OS's such as Wheezy. As requested, if I run sed -n l "/proc/$pid/comm", where $pid is $BASHPID of my terminal session, I get bash$. If I run sed -n l "/proc/$pid/cmdline", I get -bash\000$. That's a lower case L, not a one, after -n. If I run cat -vte, it hangs.

1 Answers1

2

The thing is there's no such thing as the long process name.

There's the process name (/proc/pid/comm on Linux) which on Linux is up to 15 bytes long, inherited from the parent and changed every time the process does a execve() system call to the first 15 bytes of the basename of the file being executed (but can also be changed to any arbitrary value with prctl() or by writing to that comm file).

That's what ps reports without -f or with -o comm. And what pgrep matches on by default.

And there's the list of arguments the process passed to the command it executed last (/proc/pid/cmdline on Linux). By convention, the first argument is the basename or a path to the file being executed.

ps -f or ps -o args prints the concatenation with space characters of those arguments, and pgrep -f matches on the resulting string. Because of that concatenation, you lose the information of where each argument starts and ends.

But if you can make the assumption that the first argument doesn't contain space characters, you can still match processes whose first argument contains my_long_script_name.sh with:

pgrep -f '^[^ ]*my_long_script_name\.sh'

(replace pgrep with pkill if you want to kill the corresponding processes).

For something more reliable, if on Linux, with zsh, you could do:

print -C1 /proc/<->(Ne[$'[[ ${"$(<$REPLY/cmdline)"%%\0*} = (*/|)my_long_script_name.sh ]]']:t)

That is, get the information directly from /proc/pid/cmdline, where arguments are separated by $'\0' and check that the part leading up to the first $'\0' is my_long_script_name.sh or ends in /my_long_script_name.sh.

Pipe to xargs kill if you want to kill the corresponding processes.

  • Thanks. I did not notice there is "15 bytes" limit to pgrep command before. – rustyhu Nov 21 '20 at 14:14
  • Thank you but it does not work for me. I suspect the problem is that there are characters preceeding $my_long_script_name.sh. When I do ps -Af | grep $my_long_script_name.sh, I see entries of the form: /bin/bash path_to_the_script/$my_long_script_name.sh. I don't know why these other characters are present. How can I eliminate them -- possibly by invoking $my_long_script_name.sh differently -- or how can I do a pgrep or whatever that ignores them ? – NewtownGuy Nov 21 '20 at 15:35
  • @NewtownGuy, assuming you're on Linux, can you add the output of sed -n l "/proc/$pid/comm" and sed -n l "/proc/$pid/cmdline" (where $pid is the pid of the process of that bash shell interpreting your script) to your question? – Stéphane Chazelas Nov 21 '20 at 16:10
  • I'm running Buster. I used $BASHPID for $pid when I ran the command from the command line. But I get this error message: sed: -e expression #1: character #1: missing command. Since it's -n, I followed it with a 'one'. What am I doing wrong ? – NewtownGuy Nov 21 '20 at 17:00
  • @NewtownGuy, that's a l (lowercase L), not 1 (digit one). Or use cat -vte, the idea being to clearly see what those files contain. – Stéphane Chazelas Nov 21 '20 at 19:11