Limit the output of the TOP command to a specific process name - macos

If you call the top command, you get all the running processes. But how can I limit the output only to a certain process name like "java"?
I've tried this
top -l 2 | grep java
but in this way you get only snapshots and not a continuously updated list. And top -l 0 | grep java is not really clear.

I prefer the following so I can still use top interactively without having to look up the pids each time I run it:
top -p `pgrep process-name | tr "\\n" "," | sed 's/,$//'`
Of course if the processes change you'll have to re-run the command.
Explanation:
pgrep process-name returns a list of process ids which are separated by newlines
tr "\\n" "," translates these newlines into commas, because top wants a comma-separated list of process ids
sed is a stream editor, and sed 's/,$//' is used here to remove the trailing comma

Find the pids of the processes you want to monitor and then use the -p option which allows you to provide a list of pids to the top command.
Example:
top -p 18884 -p 18892 -p 18919
PID USER PRI NI SIZE RSS SHARE STAT %CPU %MEM TIME CPU COMMAND
18884 user 25 0 672M 95M 9476 S 0.0 1.1 0:02 1 java
18892 user 25 0 2280M 123M 12252 S 0.0 1.5 0:05 1 java
18919 user 22 0 1492M 198M 28708 S 0.0 2.4 0:07 1 java
(I believe you can also pass in a comma-separated list.)

how about top -b | grep java

Expanding on #dogbane's answer, you can get all the PIDs for a named process with pgrep to do the following:
top -p "$(pgrep -d ',' java)"

Use the watch command
watch -d 'top -n1 | grep mysql'

Using the answer from here I was able to create a one liner
top -pid $(pgrep process_name | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ -pid /g')
This works for me on MacOS 10.12 (Sierra)

I solved my problem using:
top -n1 -b | grep "proccess name"
in this case:
-n is used to set how many times top will what proccess
and -b is used to show all pids
it's prevents errors like :
top: pid limit (20) exceeded

The following code updates a list of processes every 5 seconds via the watch command:
watch -n 5 -t top -b -n 1 -p$(pgrep java | head -20 | tr "\\n" "," | sed 's/,$//')

I run it (eg.): top -b | egrep -w 'java|mysqld'

Suppose .. if we have more than 20 process running on the server with the same name ... this will not help
top -p pgrep oracle | head -n 20 | tr "\\n" "," | sed 's/,$//'
It will try to list and provide real time output of 20 process where we have good chance of missing other prcesses which consumes more resource ....
I am still looking for better option on this

A more specific case, like I actually was looking for:
For Java processes you can also use jps -q whereby jps is a tool from $JAVA_HOME/bin and hence should be in your $PATH.

I came here looking for the answer to this on OSX. I ended up getting what I wanted with bash and awk:
topfiltered() {
[[ -z "$1" ]] && return
dump="/tmp/top_dump"
rm -f "$dump"
while :; do
clear
[[ -s "$dump" ]] && head -n $(( $LINES - 1 )) "$dump"
top -l 1 -o cpu -ncols $(( $COLUMNS / 8 )) | awk -v p="$(pgrep -d ' ' $#)" '
BEGIN { split(p, arr); for (k in arr) pids[arr[k]]=1 }
NR<=12 || ($1 in pids)
' >"$dump"
done
}
I loop top in logging mode and filter it with awk, building an associative array from the output of pgrep. Awk prints the first 12 lines, where line 12 is the column headers, and then every line which has a pid that's a key in the array. The dump file is used for a more watchable loop.

just top -bn 1 | grep java will do the trick for you

Running the below will give continuous update in console:
bcsmc2rtese001 [~]$ echo $SHELL
/bin/bash
bcsmc2rtese001 [~]$ top | grep efare or watch -d 'top | grep efare' or top -p pid
27728 efare 15 0 75184 3180 1124 S 0.3 0.0 728:28.93 tclsh
27728 efare 15 0 75184 3180 1124 S 0.7 0.0 728:28.95 tclsh

Here's the only solution so far for MacOS:
top -pid `pgrep java | awk 'ORS=" -pid "' | sed 's/.\{6\}$//'`
though this will undesirably report invalid option or syntax: -pid if there are no java processes alive.
EXPLANATION
The other solutions posted here use the format top -p id1,id2,id3, but MacOS' top only supports the unwieldy format top -pid id1 -pid id2 -pid id3.
So firstly, we obtain the list of process ids which have process name "java":
pgrep java
and we pipe this to awk which joins the results with delimitor " -pid "
| awk 'ORS=" -pid "'
Alas, this leaves a trailing delimitor! For example, we may so far have obtained the string "123 -pid 456 -pid 789 -pid ".
We then just use sed to shave off the final 6 characters of the delimitor.
| sed 's/.\{6\}$//'`
We're ready to pass the results to top:
top -pid `...`

get pid of process:
# pidof <process>
tell top what process pid(s) to display
# top -p <pid1>,<pid2>, etc
example:
landis#linux-e6510:~>pidof konsole
1841 1709
landis#linux-e6510:~>top -p 1841,1709
Top will only display the 2 'konsole' processes.
This is useful on a busy server via ssh, not having to pipe thru grep.

You need to feed the output of pgrep {proc_name} to top -pid {pid_No}. The problem with top -pid is that it expects -pid before each pid you want to monitor.
On Mac in zsh I can handle this problem e.g. like that:
top `pgrep proc_name | awk '{printf " -pid %d",$1}'`
It's not ideal too, because pgrep looks for substrings in process names. E.g. pgrep dd can return results for icdd, hidd, cloudd etc. The option
pgrep -x should return the exact matches only (like grep -w). But it doesn't work for me in Mac Terminal, although it does in Ubuntu virtual machine.

Using the approach mentioned in the answer by Rick Byers:
top -p `pgrep java | paste -sd "," -`
but I had more than 20 processes running so following command can be helpful for someone who encounter a similar situation.
top -p `pgrep java | head -n 20 | paste -sd "," -`
pgrep gets the list of processes with given name - java in this case. head is used to get first 20 pids because top cannot handle more than 20 pids when using -p argument. Finally paste joins the list of pids with ','.
You can control the process name you are looking for in the above command and the number of processes with that name you are interested to watch. You can ignore the head -n 20 part if the number of your processes with the given name is less than 20.

Related

Multi line invert grep match using file

I am trying to visualise all new sockets created after a save point in Debian Linux 5.14:
ss -a > state
ss -a | grep -v -f state
Expected output : Nothing
Observed output : The same as ss -a
I checked the content of the file and every line does properly end with a $ indicating it's multine.
Can't truely grasp why this happens, did anyone encounter this before ?
Use -F so the lines are treated as fixed strings and not regexes. This ensures that items like * and [::ffff:127.0.0.1] are not treated as wildcards and character classes.
For good measure, use -x to match whole lines.
$ ss -a > state
$ wc -l < state
1867
$ ss -a | grep -vxFf state | wc -l
56

How to get the PID of a process by giving the process name in Mac OS X ?

I am writing a script to monitor the CPU and MEM of any given process. For that i need to send in the name of the process to be monitored as a commandline argument. For example.
./monitorscript <pname>
I need to get the pid of the process in the script so that i can use a ps -p <pid> inside.
How do i get the pid of a process given its process name?
I understand that there might be multiple processes in the same name. I just want to get the first process out of that list.
The answer above was mostly correct, just needed some tweaking for the different parameters in Mac OSX.
ps -A | grep [f]irefox | awk '{print $1}'
You can use the pgrep command like in the following example
$ pgrep Keychain\ Access
44186
You can install pidof with Homebrew:
brew install pidof
pidof <process_name>
This solution matches the process name more strictly:
ps -Ac -o pid,comm | awk '/^ *[0-9]+ Dropbox$/ {print $1}'
This solution has the following advantages:
it ignores command line arguments like tail -f ~/Dropbox
it ignores processes inside a directory like ~/Dropbox/foo.sh
it ignores processes with names like ~/DropboxUID.sh
This is the shortest command I could find that does the job:
ps -ax | awk '/[t]he_app_name/{print $1}'
Putting brackets around the first letter stops awk from finding the awk process itself.
Try this one:
echo "$(ps -ceo pid=,comm= | awk '/firefox/ { print $1; exit }')"
The ps command produces output like this, with the PID in the first column and the executable name (only) in the second column:
bookworm% ps -ceo pid=,comm=
1 launchd
10 kextd
11 UserEventAgent
12 mDNSResponder
13 opendirectoryd
14 notifyd
15 configd
...which awk processes, printing the first column (pid) and exiting after the first match.
You can try this
pid=$(ps -o pid=,comm= | grep -m1 $procname | cut -d' ' -f1)
ps -o ppid=$(ps -ax | grep nameOfProcess | awk '{print $1}')
Prints out the changing process pid and then the parent PID. You can then kill the parent, or you can use that parentPID in the following command to get the name of the parent process:
ps -p parentPID -o comm=
For me the parent was 'login' :\
Why don't you run TOP and use the options to sort by other metrics, other than PID? Like, highest used PID from the CPU/MEM?
top -o cpu <---sorts all processes by CPU Usage

Shell scripting obtain command PID

In a shell script lets say i have run a command like this
for i in `ps -ax|grep "myproj"`
do
echo $i
done
Here, the grep command would be executed as a separate process. Then how do i get its PID in the shell script ?
I'm going out on a limb here, and understand this looks more like a comment.
Why do you need the PID of the grep command?
In your comment you say you want to compare it in the loop against something. I would suppose that it is your issue that that the loop will (sometimes) not only include myproj but also an item about your grep command? If so, try the following:
for i in `ps -ax | grep -v grep | grep "myproj"`
do
echo $i
done
The -v switch basically inverts the pattern, so grep -v grep (or grep -v "grep", which maybe looks a bit less awkward) will include only lines that do not include the string "grep" (see man grep).
Note that this maybe overly vague for some cases, for example if the pattern you actually look for also contains the string "grep". For example, the following might not work as you'd expect: ps -ax | grep -v grep | grep mygrepling
However, in your particular case, where you only look for "myproj" it will do.
Or you could simply use
for i in `ps -ax | grep "my[p]roj"`
do
echo $i
done
That way there is no need to know the PID of the grep command, because it simply never shows up as a loop iteration.
When you run a process in background, you can get its PID in $!
$ ps aux | grep dddddd & echo $!
[1] 27948
27948
ic 27948 0.0 0.0 3932 760 pts/3 R 08:49 0:00 grep dddddd
When in foreground --- the process does not exist anymore at the point you want to find its PID. When you are in the loop, the for statement is already executed and grep is already exited, so you can not find its PID anymore.

Redirect the output to two processes and merge

Background: sometimes I use ps aux | grep process_name to figure out some statistics of a process. But I can't remember the headers of ps output. So I need to do:
ps aux | tee 1.txt | grep process_name > 2.txt
cat 1.txt | head -1 | cat - 2.txt
So my questions is: is there a way to achieve this without the two temporary files, and preferably use one line of commands instead of two lines?
The simplest way is just to use something a bit smarter than grep. For example:
ps aux | perl -ne 'print if $. == 1 || m/process_name/'
will print the first line of ps aux's output, plus any line that matches process_name (which is a Perl regular expression — more powerful than POSIX BRE's, and therefore more complicated, but I don't think you'll find any surprises if you're just searching for a process name).
Edited to add: For that matter, since ps aux's headers are unlikely to change between runs, you could write:
ps aux | head -1 ; ps aux | grep process_name
though I don't know if that still counts as "one line of commands". :-)
More options: Since you don't like the above, here are some more. This one will read and print the first line directly, and then leave the rest for grep to read and process:
ps aux | ( ( read -r LINE ; echo "$LINE" ) ; grep process_name )
This one will cause every line to be written both to standard output (file descriptor 1) and to a custom file descriptor (3); then head gets standard output, while grep gets what was written to the custom file descriptor:
( ( ps aux | while read -r LINE ; do echo "$LINE" ; echo "$LINE" 1>&3 ; done | head -1 1>&4 ) 3>&1 | grep process_name ) 4>&1
That one is the most general way that I know of to do what you're asking for — at least, using only Bash builtins and features — but as you can see, it's quite unwieldy, which is why I recommend the simpler ways when you don't need the full power of this approach. Also, that last one no longer guarantees that headers come first; on my system, the output of head seems to consistently end up after the output of grep. (I suppose that could be addressed by swapping the positions of the head and grep commands, but that still wouldn't be reliable so far as I know, and it seemed to have weird effects when I tried it just now.)
Or, since I never remember perl or awk without looking it up:
ps aux | head -n 1 && ps aux |grep process_name
Another dirty trick is to memorize that the header starts with 'USER' and do
ps aux | grep -E 'USER | process_name'
This will also show the grep command itself :)
Or
ps aux | egrep "(^USER +PID)|^process_name"
sed will do exactly what you want:
ps aux | sed -n -e '1 p; /PROCESS_NAME/p'
The sed command line:
-n print only the specified lines
-e execute the following script
1 p print the first line
; command separator
/PROCESS_NAME/p print lines containing PROCESS_NAME
use the name(s) of processes with -C
ps u -C java -C totem
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
stefan 16878 0.1 10.5 429036 107868 pts/7 Sl+ 12:51 0:24 /opt/java/bin/java -Xmx256M -Xms32M -Xbootclasspa
stefan 25791 0.5 3.8 202800 38984 ? Sl 17:45 0:16 totem file:///home/stefan/Desktop/scala/dritte/dl
I hope this doesn't only work on Linux. :)
I'm always baffled to see, how many people don't know (or use) the options of ps - maybe there are too many? It's always ps | grep x. :)

How to get process id from process name?

I'm trying to create a shell script getting the process id of the Skype app on my Mac.
ps -clx | grep 'Skype' | awk '{print $2}' | head -1
The above is working fine, but there are two problems:
1)
The grep command would get all process if their name just contains "Skype". How can I ensure that it only get the result, if the process name is exactly Skype?
2)
I would like to make a shell script from this, which can be used from the terminal but the process name should be an argument of this script:
#!/bin/sh
ps -clx | grep '$1' | awk '{print $2}' | head -1
This isn't returning anything. I think this is because the $2 in the awk is treated as an argument too. How can I solve this?
Your ps -cl1 output looks like this:
UID PID PPID F CPU PRI NI SZ RSS WCHAN S ADDR TTY TIME CMD
501 185 172 104 0 31 0 2453272 1728 - S ffffff80145c5ec0 ?? 0:00.00 httpd
501 303 1 80004004 0 31 0 2456440 1656 - Ss ffffff8015131300 ?? 0:11.78 launchd
501 307 303 4004 0 33 0 2453456 7640 - S ffffff8015130a80 ?? 0:46.17 distnoted
501 323 303 40004004 0 33 0 2480640 9156 - S ffffff80145c4dc0 ?? 0:03.29 UserEventAgent
Thus, the last entry in each line is your command. That means you can use the full power of regular expressions to help you.
The $ in a regular expression means the end of the string, thus, you could use $ to specify that not only does the output must have Skype in it, it must end with Skype. This means if you have a command called Skype Controller, you won't pull it up:
ps -clx | grep 'Skype$' | awk '{print $2}' | head -1
You can also simplify things by using the ps -o format to just pull up the columns you want:
ps -eo pid,comm | grep 'Skype$' | awk '{print $1}' | head -1
And, you can eliminate head by simply using awk's ability to select your line for you. In awk, NR is your record number. Thus you could do this:
ps -eo pid,comm | grep 'Skype$' | awk 'NR == 1 {print $1}'
Heck, now that I think of it, we could eliminate the grep too:
ps -eo pid,comm | awk '/Skype$/ {print $1; exit}'
This is using awk's ability to use regular expressions. If the line contains the regular expression, 'Skype$', it will print the first column, then exit
The only problem is that if you had a command Foo Skype, this will also pick it up. To eliminate that, you'll have to do a bit more fancy footwork:
ps -eo pid,comm | while read pid command
do
if [[ "$command" = "Skype" ]]
then
echo $pid
break
fi
done
The while read is reading two variables. The trick is that read uses white space to divide the variables it reads in. However, since there are only two variables, the last one will contain the rest of the entire line. Thus if the command is Skype Controller, the entire command will be put into $command even though there's a space in it.
Now, we don't have to use a regular expression. We can compare the command with an equality.
This is longer to type in, but you're actually using fewer commands and less piping. Remember awk is looping through each line. All you're doing here is making it more explicit. In the end, this is actually much more efficient that what you originally had.
If pgrep is available on Mac, you can use pgrep '^Skype$'. This will list the process id of all processes called Skype.
You used the wrong quotes in your script:
ps -clx | grep "$1" | awk '{print $2}' | head -1
or
pgrep "^$1$"
The problem with your second example is that the $1 is in single quotes, which prevents bash from expanding the variable. There is already a utility that accomplishes what you want without manually parsing ps output.
pgrep "$1"
You can do this in AppleScript:
tell application "System Events"
set skypeProcess to the process "Skype"
set pid to the unix id of skypeProcess
pid
end tell
which means you can use 'osascript' to get the PID from within a shell script:
$ osascript -e "tell application \"System Events\"" -e "set skypeProcess to the process \"Skype\"" -e "set pid to the unix id of skypeProcess" -e "pid" -e "end tell"
3873
You can format the output of ps using the -o [field],... and list by process name using -C [command_name] ;however, ps will still print the column header, which can be removed by piping it through grep -v PID
ps -o pid -C "$1" |grep -v PID
where $1 would be the command name (in this case Skype)
I'd so something like:
ps aux | grep Skype | awk 'NR==1 {print $2}'
==== UPDATE ====
Use the parameter without quotes and use single quotes for awk
#!/bin/bash
ps aux | grep $1 | awk 'NR==1 {print $2}'
Method 1 - Use awk
I don't see any reason to use the -l flag (long format), I also don't see any reason to use grep and awk at the same time: awk has grep capability built in. Here is my plan: use ps and output just 2 columns: pid and command, then use awk to pick out what you want:
ps -cx -o pid,command | awk '$2 == "Skype" { print $1 }'
Method 2 - Use bash
This method has the advantage that if you already script in bash, you don't even need awk, which save one process. The solution is longer than the other method, but very straight forward.
#!/bin/bash
ps -cx -o pid,command | {
while read pid command
do
if [ "_$command" = "_$1" ]
then
# Do something with the pid
echo Found: pid=$pid, command=$command
break
fi
done
}
pgrep myAwesomeAppName
This works great under Catalina 10.15.2
Use double quotes to allow bash to perform variable substitution.
Single quotes disable bash variable substitution mechanism.
ps -clx | grep "$1" | awk "{print $2}" | head -1

Resources