2

Is there a ps or pgrep option or standard practice what will let me list all the running processes on a machine EXCEPT for a few specific ones I don't want to see?

Put another way -- let's say I have four processes running

% ps
PID   TTY        TIME    CMD
26712 ttys000    0:00.45 -zsh
28228 ttys001    0:00.23 -zsh
42083 ttys002    0:00.11 -zsh
42396 ttys002    0:00.16 node

and I have a text file with a few process IDs

% cat /tmp/no-interest.txty 
26712
28228

I want a command line invocation that will display just the process information for 42083 and 42396.

The more general problem I'm trying to solve is I want to show processes that started AFTER a certain point in time. I can get as far as building a list of currently running processes in a text file, but I'm blocked a bit on how to come up with a concise way to use this information to suppress the processes I don't want to see. I know how to do this with a short program in a variety of languages, but I'd like something I can invoke as a one liner and not have to worry about getting that program onto the specific machine I'm debugging.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
Alana Storm
  • 1,413
  • 3
  • 14
  • 17
  • 1
    Are the processes that you want to list _related_ to each other? Are they, for example, in the same process group, or do they have the same parent process? Do you know one or several command names that you want to look for? – Kusalananda Jan 30 '23 at 23:50
  • @Kusalananda Good questions. And the answer are no, no, and no. The use case is I hop onto a machine (virtual or otherwise) and want to see anything started AFTER I've hopped onto the machine. – Alana Storm Jan 30 '23 at 23:56
  • 1
    You could use this command `ps -eo pid,user,start,cmd | awk '$3>"17:40"'`. But there is a problem here, it will show everything from `17:40:00` till `23:59:59` (not `00:00:01`). In that case you should use `lstart` instead `start` and this cmd `ps -eo pid,user,lstart,cmd | awk '$5>30 || $6>="17:40"'`. But again, the same problem if today is the last day of this month and you work late (to midnight and beyond) :). – Damir Jan 31 '23 at 02:01
  • 1
    By default `ps` is sorted by `pid` and due to `pid` and `lstart` match in terms of "sorting-increasing", you could use `pid` of the time you are interested in. Let's say that process with `7895` pid has `STARTED` time you are interested in: `ps -eo pid,user,lstart,cmd | awk '$1>=7895'`. With this cmd you get rid of "midnight/last/next day". – Damir Jan 31 '23 at 02:46
  • @AlanStorm are you root on that machine? – Marcus Müller Feb 01 '23 at 09:22
  • 1
    @AlanStorm, reading your question again... Do you actually need this: `ps | sed "1,/$$/d"`? It will suppress everything "above" your current shell (including your current shell). – Damir Feb 01 '23 at 14:28
  • 1
    @AlanStorm, actually it seems you need this: `ps | sed -n /$$/',$p'` -suppress everything "above" last/current shell, print current shell and everything "below". – Damir Feb 01 '23 at 14:52

3 Answers3

3
ps | grep -vFwf /tmp/no-interest.txty

Would filter out the lines of ps which contain any of the lines (word delimited) in /tmp/no-interest.txty. Note however that it looks for them anywhere, not only in the pid field.

Since your shell seems to be zsh, if your ps accepts taking a list of pids as arguments, you could also use zsh's array subtraction operator:

to_exclude=( $(</tmp/no-interest.txty) )
all_pids=( $(ps -o pid=) )
ps -p ${all_pids:|to_exclude}

Or using an anonymous function:

to_exclude=( $(</tmp/no-interest.txty) )
() { ps -p ${@:|to_exclude}; } $(ps -o pid=)

$(</tmp/no-interest.txty) splits the contents of that file on $IFS characters. See also ${(f)"$(</tmp/no-interest.txty)"} for every non empty line in the file.

In any case, you can't rely on pid number to know their start order as pid numbers eventually wrap.

With some ps implementations, you can use:

ps -o lstart

To get the process start time with second precision (in Tue Jan 31 06:05:22 2023 format on my system which uses the ps from procps-ng; in English regardless of the locale). You can parse it with zsh's strftime builtin in the C locale:

zmodload zsh/datetime
(( cutoff = EPOCHSECONDS - 2*60*60 )) # two hours ago
pids=()
ps -o pid= -o lstart= |
  while read -r pid start; do
    LC_ALL=C strftime -rs t '%a %b %d %H:%M:%S %Y' $start &&
      (( t >= cutoff )) &&
      pids+=( $pid )
  done
(( $#pids )) && ps -p $pids

Also beware that the start time of a process is not necessarily the same as the time it (or one of its ancestors) invoked the command it's currently running, as processes can and often do run more than one command in their life time.

For instance, in sh -c 'sleep 2; exec env cmd' it's the same process that runs, sh, then env, then cmd (and it likely started of as child of your interactive shell).

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
2

want to see anything started AFTER I've hopped onto the machine.

ahahahaa! So you would probably also prefer the things that are so short-lived that your probability of intercept¹ them with a single "snapshot" of all running processes is very low.

So, instead of looking at which processes are running now, what about a log of all processes started since you you logged on?

You'll need bpftrace (installed on many system, available on basically any Linux distro). A nice script to show all attempts to start a process by executing a file:

#!/usr/bin/bpftrace
// Taken from https://unix.stackexchange.com/a/584857/106650
// by Rui F Roberto CC-BY-SA 4.0
// Modified to log exec syscalls by Marcus Müller
BEGIN {
  printf("Tracing exec syscalls... Hit Ctrl-C to end.\n"); 
  printf("%-9s %-6s %-16s %s\n", "TIME", "PPID", "COMM", "EVENT");
}

tracepoint:syscalls:sys_enter_exec*
{
  time("%H:%M:%S ");
  printf("%-6d %s: ", pid, comm);
  join(args->argv);
}

Less pretty, but a one-liner to copy&paste might always come in handy:

bpftrace -e 'tracepoint:syscalls:sys_enter_exec* { printf("%-6d %s: ", pid, comm); join(args->argv); }'

¹ A term from the world of spectrum analyzers and such – signals that "pop up" for a very short duration in bands and at times you don't know beforehand are pretty hard to "catch" if you have to tune through your bands sequentially and "take a snapshot" of each. Hence, signal detection people look at a sensing system and a class of signals and say things like "you get a probability of intercept of only 0.1%".

Marcus Müller
  • 21,602
  • 2
  • 39
  • 54
  • Oh, sweet! I've been looking for an excuse to get into BPF/eBPF and this might be it. Thank you! – Alana Storm Feb 02 '23 at 23:10
  • Note that the format of the OP's tty device names would seem to suggest they're not using a Linux-based OS. Probably macos – Stéphane Chazelas Feb 03 '23 at 08:14
  • thanks again for this answer -- while it's super useful information of interest to me it doesn't help with my very weird and specific use case, where I'm connected to the VM that Docker Desktop uses on MacOS and trying to observe the actual process that running commands on my mac starts on this VM. It appears this VM doesn't have bpftrace on it, and I'm wary of adding too many programs to this VM lest I hose it. – Alana Storm Feb 06 '23 at 22:45
  • Hmm you could write a small program that uses libpftrace and statically link it, I think. Never tried that before, though! – Marcus Müller Feb 07 '23 at 07:52
1

To switch my comments to answer.

  • Using ps' start and awk

To list the all processes after the specific time till the end of the current day (midnight):

ps -eo pid,user,start,cmd | awk '$3>"17:40"'

To list the all processes after the specific time until the end of the next day (next day midnight):

ps -eo pid,user,lstart,cmd | awk '$5>30 || $6>="17:40"'
#cons: will not work as you would like if the next day is the first day of next month.
  • Using ps' pid and awk

Not so clever :), but you could firstly see the pid of the process started time you are interested in, and then awk (sed also, but awk is better here) to filter them.
To list all the processes from the specific time till the last process running (no matter how many hours/days between "specific time" and the last process):

ps -eo pid,user,lstart,cmd | awk '$1>=7895'

Quick explanation:

  • start and lstart - also bsdstart, start_time and stime (latter two look the same to me; lstart provides the longest time format) - ps' standard format specifiers used to display the time when command was started.
  • '$3>"17:40"' - if field 3 is greater than 17:40 print the whole line (record), default action is {print}, so we don't need to type it explicitly.
Damir
  • 491
  • 2
  • 4