40

The Linux ps command shows different memory usages like RSS (resident set size), size in kB by default. Is there a way to show in MB or GB, like ls -s --human-readable does?

1
  • 1
    The lack of mention in man ps suggests to me that there is no such built-in option. Commented Jun 21, 2017 at 6:24

2 Answers 2

44

AFAIK you cannot achieve it simply by pure ps command with options. However you can use some text processors like awk and make it to do what you want:

ps afu | awk 'NR>1 {$5=int($5/1024)"M";}{ print;}'

This takes result from ps and then for every line except the first one it replacecs 5th column which is in KB normally, to MB adding M suffix.

You can make it an alias and store it in .bashrc file so you can call it by something like myps.

Most of people are asking how to preserve format or use other units and precision.

For simple version you can use column -t output filter:

ps afu | awk 'NR>1 {$5=int($5/1024)"M";}{ print;}' | column -t

This however does not recognize spaces in last column correctly. Unfortunately we've got to deal with text formatting and prepare our own format string in printf -like format.

ps afu | awk 'NR==1 {o=$0; a=match($0,$11);}; NR>1 {o=$0;$5=int(10*$5/1024)/10"M";}{ printf "%-8s %6s %-5s %-5s %9s %9s %-8s %-4s %-6s %-5s %s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, substr(o, a);}'

Explanation:

  • NR==1 condition is for first line only (header). We are using original ps output to determine where COMMAND is starting:
    • o=$0 stores unmodified entire line so we can use it later
    • a=match($0,$11) finds location of 11th field (which should be where COMMAND column is starting in original output)
  • NR>1 is for following lines (data). We are changing 5th field:
    • $5=int(10*$5/1024)/10"M" changes value into Megabytes with one decimal place and adds "M" suffix.
    • printf displays all fields in column-like flavor:
      • %-10s means s for string, 10 for 10 characters wide, - for left align
      • %8s means s for string, 8 for 8 characters wide, and because of no - output of this field is right-aligned.
    • substr(o, a) takes substring of original line (hence o stored before) starting from position a calculated in previous condition, so we can have command output displayed with spaces preserved.
5
  • 7
    Modified for GB and with 2 decimal places (e.g. 1.23GB): ps aux | awk '{$5=int(100 * $5/1024/1024)/100"GB";}{ print;}'
    – mahemoff
    Commented Dec 19, 2018 at 12:45
  • 2
    is it possible to achieve the same goal preserving the ps formatting? Tabs are completely stripped away with awk
    – Chris
    Commented Aug 22, 2019 at 10:24
  • I see you are casting as an int, is it possible to have it decimal like 1.2M ? Commented Aug 30, 2019 at 20:20
  • Why tabs are missing?
    – Alex78191
    Commented Apr 3, 2020 at 18:22
  • @Alex78191: Added alternate command for formatting with table-like output
    – DevilaN
    Commented Apr 6, 2020 at 9:30
8

I came here looking for a solution to the same problem. It's surprising there isn't yet a better answer for this. I created my own slightly more flexible fix based on this thread and other pages I read on the web.

I to make things more human readable I made an auto ranging function and used that on the vsz and rss fields.

Also, it looks like there IS actually a way to force an arbitrary field separator on ps. Here is how I did it:

I discovered an apparently undocumented trick while playing around with the field descriptors located in the AIX FORMAT DESCRIPTORS sub heading of the ps manpage. It seams that it will include any characters before the % on each output line when you do something like %U for USER or %z for VSZ for example. I wondered what would happen if I tried %%. It showed a % on every line. OK, how about an arbitrary string without spaces? YES!! BUT apparently you HAVE to have %% in the string for things to work predictably. % doesn't get it. Beware that ps will try to parse everything after the % to see if its a valid field descriptor. Because of this, it is probably safest to keep it at the end of the string like this FOOBARBIGSTRINGBLABLA%% to avoid issues.

Now I just needed to come up with a string that has a relatively low chance of being a part of valid data - especially if I use cmd as a field. zzz:::zzz seems highly unlikely. lets just add the extra %% at the end to make it work - zzz:::zzz%% which shows up in the output as zzz:::zzz%.

I process the output with awk which can handle multi-character field separators, telling awk to look for zzz:::zzz% as the field separator.

|head -n20 and |cut -c -250 at the end is to limit the output to 20 lines and cut each line after 250 characters to keep things neat for my terminal. For actual scripting purposes you would likely want to remove these.

EDIT: I added a variable to call out the fact that you can also sort by any field using these examples. The names for the fields can be found in the manpage for ps under the heading STANDARD FORMAT SPECIFIERS

EDIT 2: As requested, I added a more in-depth explanation about how the arbitrary field separator works and my reasoning for choosing the field separator I chose.

EXAMPLE 1

sortbyfield="rss"; fsep="-o zzz:::zzz%% -o"; ps ax o user:16 $fsep pid $fsep pcpu $fsep pmem $fsep vsz $fsep rss $fsep tty $fsep stat $fsep lstart $fsep time:16 $fsep cmd  --sort -$sortbyfield | awk 'function setprefix(num){{n_suffix=1; while(num > 1000 && n_suffix < suffixes_len) {num /= 1024; n_suffix++;}; num=int(100*num)/100suffixes[n_suffix]}; return num} BEGIN{suffixes_len=split("kB MB GB TB PB EB ZB",suffixes);FS="zzz:::zzz%";} NR>1 {$5=setprefix($5);$6=setprefix($6); }{ printf "%-16s %6s %-5s %-5s %9s %9s %-8s %-8s %-25s %-18s %s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11;}' |head -n20 |cut -c -250

OUTPUT: (slightly sanitized example)

USER                PID %CPU  %MEM        VSZ       RSS TT       STAT                      STARTED              TIME   CMD
gdm                1474  0.0   0.3     2.87GB  182.86MB tty1     Sl+      Sat Jun 18 01:31:44 2022          00:34:40   /usr/bin/gnome-shell
gdm                1370  0.0   0.0    171.3MB   34.31MB tty1     Sl+      Sat Jun 18 01:31:43 2022          00:01:56   /usr/libexec/Xorg vt1 -displayfd 3 -auth /run/user/42/gdm/Xauthority -background none -noreset -keeptty -verbose 3
gdm                1552  0.0   0.0   686.07MB   20.14MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:40   /usr/libexec/gsd-color
gdm                1577  0.0   0.0   870.49MB   19.53MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:01   /usr/libexec/gsd-media-keys
gdm                1538  0.0   0.0   531.81MB   18.51MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:00   /usr/libexec/gsd-xsettings
gdm                1541  0.0   0.0   539.53MB    18.5MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:04   /usr/libexec/gsd-power
gdm                1570  0.0   0.0    458.5MB    18.4MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:00   /usr/libexec/gsd-wacom
gdm                1500  0.0   0.0   386.57MB   17.94MB tty1     Sl       Sat Jun 18 01:31:45 2022          00:00:00   /usr/libexec/ibus-x11 --kill-daemon
gdm                1537  0.0   0.0   386.13MB   17.92MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:00   /usr/libexec/gsd-clipboard

EXAMPLE 2

Slightly less verbose version:

sortbyfield="rss"; fsep="-o zzz:::zzz%% -o"; ps ax o user:16 $fsep pid $fsep pcpu $fsep pmem $fsep vsz $fsep rss $fsep tty $fsep stat $fsep lstart $fsep time:16 $fsep comm  --sort -$sortbyfield | awk 'function setprefix(num){{n_suffix=1; while(num > 1000 && n_suffix < suffixes_len) {num /= 1024; n_suffix++;}; num=int(100*num)/100suffixes[n_suffix]}; return num} BEGIN{suffixes_len=split("kB MB GB TB PB EB ZB",suffixes);FS="zzz:::zzz%";} NR>1 {$5=setprefix($5);$6=setprefix($6); }{ printf "%-16s %6s %-5s %-5s %9s %9s %-8s %-8s %-25s %-18s %s\n", $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11;}' |head -n20 |cut -c -250

OUTPUT: (slightly sanitized example)

USER                PID %CPU  %MEM        VSZ       RSS TT       STAT                      STARTED              TIME   COMMAND
gdm                1474  0.0   0.3     2.87GB  182.86MB tty1     Sl+      Sat Jun 18 01:31:44 2022          00:34:40   gnome-shell
gdm                1370  0.0   0.0    171.3MB   34.31MB tty1     Sl+      Sat Jun 18 01:31:43 2022          00:01:56   Xorg
gdm                1552  0.0   0.0   686.07MB   20.14MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:40   gsd-color
gdm                1577  0.0   0.0   870.49MB   19.53MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:01   gsd-media-keys
gdm                1538  0.0   0.0   531.81MB   18.51MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:00   gsd-xsettings
gdm                1541  0.0   0.0   539.53MB    18.5MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:04   gsd-power
gdm                1570  0.0   0.0    458.5MB    18.4MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:00   gsd-wacom
gdm                1500  0.0   0.0   386.57MB   17.94MB tty1     Sl       Sat Jun 18 01:31:45 2022          00:00:00   ibus-x11
gdm                1537  0.0   0.0   386.13MB   17.92MB tty1     Sl+      Sat Jun 18 01:31:46 2022          00:00:00   gsd-clipboard

Hopefully this helps someone out there!

3
  • 2
    (1) We don’t give bonus points for most characters on one line.  Please format scripts as multiple lines so as to avoid the need for horizontal scrolling. (2) It seems that you have chosen zzz::zzz as an arbitrary string, but the % part is special.  Please explain how this works. … … … … … … … Please do not respond in comments; edit your answer to make it clearer and more complete. Commented Sep 6, 2022 at 21:55
  • 2
    I'm not sure how I can break up the code and still have it make sense. Some of the stuff I'm doing is just loooong. What would you suggest?
    – Kronos
    Commented Sep 6, 2022 at 23:48
  • The first two statements can be on separate lines. The head and cut statements aren't relevant to the question and can just be left out, if really wanting to keep them in, escaping the new line and putting them on another line makes sense. Then define the parameters to ps on a line so that can be split up. Lastly defining awk functions to be called later and doing it as a script is probably best.
    – LovesTha
    Commented Feb 1, 2023 at 0:33

You must log in to answer this question.

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