6

I want to get information about all processes in the system except the ps process itself.

That is, something like:

ps --no-headers --pid 1111 -N

where 1111 is the process id of the currently running ps command.

I tried something like

ps --no-headers --ppid $$ -N

However, this does not work when ps is running in a "subprocess" like:

IFS=' ' read pctd proc <<<$(/bin/ps --no-headers --ppid $$ -No pcpu,pid,comm --sort=+pcpu | tail -n 1)

This last command tries to get the CPU usage percentage and process id plus name of the process using the most CPU "recently".

IFS=' ' read pctd proc <<<$(/bin/ps --no-headers -eo pcpu,pid,comm --sort=+pcpu | tail -1)

was working OK until recently. However, after update to Ubuntu 23.04 (or kernel 6.x) ps started sometimes listing itself as using 100% CPU.


For the time being, the following seems as a workaround (but it is not safe):

IFS=' ' read pctd proc <<<$(/bin/ps --no-headers -eo pcpu,pid,comm --sort=+pcpu | grep -v \ ps | tail -1)
1
  • 3
    What are you really trying to achieve in the last line? Apr 22 at 17:54

4 Answers 4

13

To exclude ps itself:

sh -c 'exec ps --no-headers --pid "$$" -N'

sh substitutes its own PID for $$, but then it replaces itself with ps. PID does not change, this is how exec works. This way ps receives its own PID as the option argument to --pid.

Note this method excludes the ps itself but not other ps processes (if any).

9
  • Unfortunately this does not work also: bash -c 'exec /bin/ps --no-headers --ppid "$$" -No pcpu,pid,comm --sort=+pcpu' | tail -1 produces (sometimes) 100 7321 ps . :(
    – FedKad
    Apr 22 at 19:41
  • 3
    @FedKad --pid and --ppid are not equivalent. Apr 22 at 20:48
  • Thanks. That is a very clever solution. In my case, the code is something like IFS=' ' read pctd proc <<<$(sh -c 'exec /bin/ps --no-headers --pid "$$" -No pcpu,pid,comm --sort=+pcpu' | tail -n 1) ; echo $pctd $proc. I will accept this answer, if there is no other shorter one.
    – FedKad
    Apr 23 at 8:11
  • In bash you can use ... $(exec ps --pid "$BASHPID" ... ) instead, no need of an additional sh/bash process
    – LL3
    Apr 23 at 9:51
  • Sorry, but I tried the command IFS=' ' read pctd proc <<<$(exec /bin/ps --no-headers --pid "$$" -No pcpu,pid,comm --sort=+pcpu | tail -n 1) ; echo $pctd $proc, but it did report ps.
    – FedKad
    Apr 23 at 10:02
4

As a generalisation of how to remove ps entry for itself, especially for ps implementations that don't support the non-standard -N/--deselect option, a common approach is to do:

print_pid_and_run() {
  sh -c 'echo "$$" && exec "$@"' sh
}
print_pid_and_run ps ...ps-options... |
  awk 'NR == 1 {pid = $0; next}
       $1 != pid'

(change $1 with the actual field that has the process pid in ps' output depending on what option is passed to ps).

In zsh, running sh can be avoided by doing:

zmodload zsh/system
print_pid_and_run() (echo $sysparams[pid] && exec "$@")

In bash:

print_pid_and_run() (echo "$BASHPID" && exec "$@")

(bearing in mind that in bash, contrary to zsh above, that can't be used to run builtins or functions).

2

Not an answer to your question, but a comment on your:

IFS=' ' read pctd proc <<<$(/bin/ps --no-headers --ppid $$ -No pcpu,pid,comm --sort=+pcpu | tail -n 1)

This last command tries to get the CPU usage percentage and process id plus name of the process using the most CPU "recently".

I doesn't and your approach is misguided there. pcpu returns the ratio of the time any CPU spent to run any thread of the process to the total time since the process came into existence.

So for instance, a process could start, spend a few ms initialising itself like when executing a command (during which it may use close to 100% of one CPU for 1ms), then sleep for 10 hours, then spawn 20 threads fully utilising 20 CPUs for 1 minute (during which top for instance would show 2000% CPU usage). If you run ps -o pcpu at that point, the process will have used 20 minutes of CPU over 601 minutes total, so you'll see something like 3%, not 2000%. If on the other hand, you happen to run that ps at the very moment a sleep 1h command is started, ps may find that that process has used 1 millisecond of CPU time over 1 millisecond, and report 100% CPU utilisation.

If you want to get the process that is still running and has been using the most CPU time over the last say 3 seconds, you'd need to collect the CPU times of all processes at one point, then again 3 seconds later and do the substractions, you can't use ps -o pcpu for that, or you could use some top-like application in batch mode which would do the calculation for you.

1
  • Yes. You are right on this. And thank you for the clarification! However, my question should be considered only for what I ask in the title.
    – FedKad
    Apr 23 at 10:49
2

Like this, using filter to discard ps:

$ read pctd pid proc < <(
    ps --no-headers -eo pcpu,pid,comm |
        awk '$1>=max && $3!="ps"{max=$1; val=$0}END{print val}'
)

$ echo $pctd 
11.1
$ echo $pid
1234
$ echo $proc 
process-name

A more reliable way, is to use top:

top -b -n1 | awk '/ PID /{p=1;next} p && !/top/{print $9, $12;exit}'
8
  • Unfortunately the < <(...) construct has the same problem. :(
    – FedKad
    Apr 22 at 18:26
  • The awk way works (because it does not run in a subprocess). However, I need to get the output into variables.
    – FedKad
    Apr 22 at 18:30
  • What is "not reproducible"? The ps command reporting itself using 100% CPU? You should try several times. It does not always act the same!
    – FedKad
    Apr 22 at 19:00
  • 1
    Thanks; that is an option to consider. Of course, there is no guarantee that the only ps being ignored is the one we want!
    – FedKad
    Apr 23 at 8:59
  • 1
    Check top added command Apr 23 at 12:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .