简体   繁体   中英

Capture %CPU and PID of processes filtered by COMMAND using top command

I need to write a Bash script that does the following:

  1. In the "top" command, I would like to filter the processes by a given COMMAND. In the following I use Google Chrome as an example, which appears as "chrome" in the COMMAND column.
  2. After filtering, there can be zero, one or more processes with COMMAND "chrome" left (this is just to highlight that there is not exactly one process with COMMAND "chrome" in general).
  3. Now I would like to write the current time (hh:mm:ss), the PID of the process and the %CPU value displayed for this process to a file "logfile"
  4. Repeat steps 1 to 3 once every second.

Example: Assuming that there are three "chrome" processes, the output in "logfile" should look something like below (for the first three seconds):

    17:49:12 7954 14.0
    17:49:12 7969  9.3
    17:49:12 2626  1.3
    17:49:13 7954 12.0
    17:49:13 7969  6.3
    17:49:13 2626  1.2
    17:49:14 7954 14.7
    17:49:14 7969  8.5
    17:49:14 2626  2.1

My ideas so far: Using the command

    top -b -n 1 -p 7954 | tail -n 2 | head -n 2 | awk '{print $1, $9}' >> logfile

I filter top by PID (in this case PID == 7954) and the output looks like

    PID %CPU
    7954 6.6

however (since I actually want to filer by COMMAND) I do not know how to filter by COMMAND. In the line above, the "-p 7954" does the filtering for PID==7954, however what do I need to write here to filter by COMMAND==chrome? Also, How can I remove/avoid the header?

According to the time step: I found that the command

    date +"%T" 

gives me the time in the correct format (hh:mm:ss).

So I just struggle with putting these pieces together and fix the filtering problem mentioned above. Thank you for any help!

Awk can do this; awk '/regex/ { print }' performs the print action only on lines matching regex .

However, you can (and perhaps also should) subsume head and tail as well:

top -b -n 1 | awk 'NR>1 && $10 == "chrome" {print strftime("%T"), $1, $9}' 

... assuming the tenth field of top output contains the command name.

however what do I need to write here to filter by COMMAND==chrome

Write a small script to accomplish this, say calc_proc_mem which looks like below :

#!/bin/bash
if [ -z "$1" ] #checking if first param exist
then
  echo "Usage : cal_proc_mem process_name"
  exit 1 # Exiting with a non-zero value
else
  proc_ids=( $(pgrep "$1") )
  if [ ${#proc_ids[@]} -eq 0 ] #checking if if pgrep returned nothing
  then
    echo "$1 : Process Not Running/No such process"
    exit 1 # Exiting with a non-zero value
  else
    echo "$1's %CPU-%MEM usage as on $(date +%F)" >> logfile
    while true
    do
      for proc_id in "${proc_ids[@]}"
      do
      usage="$(ps -p "$proc_id" -o %cpu,%mem | awk -v pid=$proc_id 'NR==2{printf "PID : %-10d \%CPU : %f \%MEM : %f\n",pid,$1,$2}' 2>/dev/null)"
      echo -e "$(date +%H:%M:%S)\t$usage" >> logfile
      done
      sleep 3
    done 
  fi
fi

Run the script as

./calc_proc_mem process_name

Sample Output

chrome's %CPU-%MEM usage as on 2016-06-27
23:40:33    PID : 3983       %CPU : 1.300000 %MEM : 2.200000
23:40:33    PID : 8448       %CPU : 0.100000 %MEM : 4.300000
23:40:33    PID : 8464       %CPU : 0.000000 %MEM : 0.400000
23:40:33    PID : 8470       %CPU : 0.000000 %MEM : 0.200000
23:40:33    PID : 8526       %CPU : 0.000000 %MEM : 3.000000
23:40:33    PID : 8529       %CPU : 0.000000 %MEM : 0.200000
23:40:33    PID : 8563       %CPU : 0.000000 %MEM : 1.500000
23:40:33    PID : 8655       %CPU : 0.300000 %MEM : 4.900000
23:40:33    PID : 32450      %CPU : 0.300000 %MEM : 2.100000

Note

Since you've an infinite while-loop running, you need to manually terminate the program using Ctrl C.

You can drop '-p PID' option and next, grep by COMMAND. You can do the next:

top -b -n 1 | grep 'chrome' | tail -n 2 | head -n 2 | awk '{print $1, $9}'

Another command sample to get you going could be:

$ cmd="sleep"; for j in {1..3}; do (${cmd} 123 &); done; 
$ ts=$(date +"%T"); top -b -n 1| sed s/^[^\ 0123456789].*$//g |grep "${cmd}"|tr -s '\n'| awk '{print $1, $9, $12}'|sed s/^/"${ts} "/g
19:36:51 35122 0.0 sleep
19:36:51 35124 0.0 sleep
19:36:51 35126 0.0 sleep

It prints the time as given by the date call, and from top: PID, %CPU, and COMMAND field found. The headers and non matching data lines are filtered via sed (no number at start, which could suppress small pids by the way =( thus also a space at line start is accepted) and grep on the command. The time is prepended py sed on start of line injecting the stored timestamp and a blank space to separate.

It is not elegant but might fit your needs to have a start.

The pgrep solutions or using awk with a regex look better ... but at least I enjoyed trying to solve it with top. The tail and head stages in the pipe look suspicious to me ...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM