4

I am writing a shell script to check the vmstat si and so data at various time intervals

vmstat 1 sample output:

procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
0  0  45820 899252  86700 468520    0    0     0    60  127 5821 20  7 34  0
0  0  45820 899252  86704 468504    0    0     0    32   44  104  0  0 100  0

I want to use awk, sed to extract si and so into different variables for further usage. I Am new to awk, sed and am still struggling to find my way through. Can you show me how I can do this ?

user9517
  • 114,104
  • 20
  • 206
  • 289
APZ
  • 954
  • 2
  • 12
  • 24

3 Answers3

3

This AWK script reads the second line and uses the field headers as indices into the data on each line so you can refer to them by name. Here is the one-liner. I break it out line by line below.

vmstat -n 1 | awk 'NR == 1 {next} NR == 2 {for (i = 1; i <= NF; i++) fields[$i] = i; next} {split($0, data); item = data[fields["si"]]; print item; totals[fields["si"]] += item} NR >= 6 + 2 {exit} END {print "Average", totals[fields["si"]]/(NR - 2)}'

As shown, it prints the contents of the "si" column and the average at the end. You could handle multiple fields or iterate over all the fields.

You can expand on this to handle other fields, do comparisons of one line to the previous one or do totals or other calculations. You can stop, as I have shown, after a certain number of records.

The -n option to vmstat causes the header to only be printed once.

The breakdown:

vmstat -n 1 | awk 'NR == 1 {next}    # skip the first line
# for the second line, create an array of field positions indexed by the
# name of the field then do "next" so this line is not processed further
NR == 2 {for (i = 1; i <= NF; i++) fields[$i] = i; next} 
{split($0, data); # split the line of values into an array
item = data[fields["si"]];    # pick out an item based on its name
print item;
totals[fields["si"]] += item}    # accumulate a total
# exit when the number of desired records (plus the two header lines) 
# has been read, you could use a vmstat argument to do this instead
NR >= 10 + 2 {exit}
# calculate and print the average (subtract the two header lines from the record count)
END {print "Average", totals[fields["si"]]/(NR - 2)}'
Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
1
SWAPIN=$(vmstat | egrep -v 'swap|si' | awk '{ print $7 }')
SWAPOUT=$(vmstat | egrep -v 'swap|si' | awk '{ print $8 }')

or more "standard" way:

$ vmstat | awk '{ for (i=1; i<=NF; i++) if ($i=="si") { getline; print $i }}'
  • The built-in NF variable gives you the number of fields in the current line
  • getline reads the next input line
quanta
  • 50,327
  • 19
  • 152
  • 213
0

I would feel remiss if I didn't supply the perl oneliner equivalents:

SWAPIN=$(vmstat | perl -lane 'next if /^(procs|r)/; print $F[7]')
SWAPOUT=$(vmstat | perl -lane 'next if /^(procs|r)/; print $F[8]')
Phil Hollenback
  • 14,647
  • 4
  • 34
  • 51