Script how to show memory usage

This is no a tutorial in the real sense but just a script to show memory usage.
pkg install zsh
pkg install gawk

Code:
#!/usr/local/bin/zsh
/bin/date
W=`/sbin/sysctl vm.stats.vm.v_wire_count    | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
A=`/sbin/sysctl vm.stats.vm.v_active_count  | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
L=`/sbin/sysctl vm.stats.vm.v_laundry_count | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
I=`/sbin/sysctl vm.stats.vm.v_inactive_count| /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
C=`/sbin/sysctl vm.stats.vm.v_cache_count   | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
F=`/sbin/sysctl vm.stats.vm.v_free_count    | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
T=`/sbin/sysctl vm.stats.vm.v_page_count    | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
T2="$(( $W + $A + $L + $I + $C + $F))"

Z=`/sbin/sysctl kstat.zfs.misc.arcstats.size | /usr/local/bin/gawk '{$2=$2*4096/1024/1024;printf("%04.0f\n",$2);}'`
G=`echo "$(( $T -$T2))"                     | /usr/local/bin/gawk '{$1=$1*4096/1024/1024;printf("%04.0f\n",$1);}'`
echo "   Wired:"$W"M"
echo "  Active:"$A"M"
echo " Laundry:"$L"M"
echo "Inactive:"$I"M"
echo "   Cache:"$C"M"
echo "    Free:"$F"M"
echo "     Gap:"$G"M"
echo "--------------"
echo "   Total:"$T"M"
echo "   ARC  :"$Z"B"
 
Ugh, yes! That’s more readable than the abbreviations employed by vmstat(8) or top(1). Maybe this script would’ve been a great addition to Thread useful-scripts.737? 📖 But, uh‐oh, there’s a bug: kstat.zfs.misc.arcstats.size is not a page count. It is already in bytes.

It’s surprising, though, that while you depend on zsh(1) you don’t make no use of its advanced features, in particular its built‐in float support.​
Bash:
#!/usr/bin/zsh
printf '% 6.1f\n'  $((456 * 4096.0 / 1024.0 / 1024.0))
That is usually the reason why (when programming sh(1)ell scripts) you’re “forced” to invoke other programs (like gawk(1)), you know, because the $((arithmetic expansion)) is limited to integers. 🧮

I guess I would’ve probably gone for *4096/1024/1024 myself, too (in conjunction with bc(1)/dc(1) though), 🗜️ but Thread working-with-numbers-on-commandline.98848 reminded me of the existence of units(1) (the version part of the base system, not the math/units port sidetone mentioned in said thread), so I gave it a try and adapted the script as follows:​
Bash:
#!/bin/sh -eu
#        name: memory usage
# description: pretty‐prints assorted memory usage information
#      author: N N <mailbox@host> [fill out esp. in _multi_‐sysadmin environments]

# Converts amount of bytes to mebibytes and attaches unit suffix.
mebibytes() {
    # As of FreeBSD version 14.3, units(1) does not like to convert ℤ ∖ ℕ.
    if [ "${1}" -ne 0 ]
    then
        # As of FreeBSD version 14.3, units(1) does not understand
        # a double dash (`--`) end of options indicator. Actually,
        # no argument should be negative though, so … never mind.
        result=$(units -o '%f' -t "${1#-} bytes" 'mebibytes')
    fi
 
    # NB: `M`/`MB` can be misunderstood as 1,000,000 B, compare orders(7).
    printf '% 6.f MiB' ${1%%[1-9]*}${result:-}
}

# -n : no name = just show values
# Should any OIDs be(come) unavailable, the `-e` sh(1) flag aborts execution.
sysctl -n \
    'hw.pagesize' \
    'kstat.zfs.misc.arcstats.size' \
    'vm.stats.vm.v_page_count' \
    'vm.stats.vm.v_active_count' \
    'vm.stats.vm.v_cache_count' \
    'vm.stats.vm.v_free_count' \
    'vm.stats.vm.v_inactive_count' \
    'vm.stats.vm.v_laundry_count' \
    'vm.stats.vm.v_wire_count' \
| {
    # -r : do not interpret backslash as escape
    # [not that there are any, but I like to disable unneeded features]
    read -r pagesize
    read -r arc
 
    for metric in total active cache free inactive laundry wired
    do
        eval read -r ${metric}
    
        eval ${metric}=$((${metric} * pagesize))
        # `total` is multiplied by the same `pagesize` factor,
        # hence this repeating operation is included in the loop,
        # but `total` is to be excluded from `sum`:
        #
        # During the first iteration `$sum` is undefined
        # so the default value `-1 * total` applies;
        # adding ` + total` again results in a zero `sum`.
        eval sum=$((${sum:--1 * total} + ${metric}))
    done
 
    # No `echo`ing: Here-documents by default support (some) interpolation.
    # (If you do not want that, put the delimiter word in single quotes.)
    # The dash in `<<-` strips common leading tabulation characters.
    cat <<- EOT
       Wired ≈ $(mebibytes ${wired})
      Active ≈ $(mebibytes ${active})
     Laundry ≈ $(mebibytes ${laundry})
    Inactive ≈ $(mebibytes ${inactive})
       Cache ≈ $(mebibytes ${cache})
        Free ≈ $(mebibytes ${free})
         Gap ≈ $(mebibytes $((total - sum)))
    ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
       Total ≈ $(mebibytes ${total})
         ARC ≈ $(mebibytes ${arc})
    EOT
}
Unfortunately, my version doubled the number of lines of code (as reported by misc/sloccount) and may be perceived as overengineered. 😭
 
Back
Top