Created
January 29, 2021 09:54
-
-
Save aaronNGi/5aacadb072b356aab261e292a7be30e6 to your computer and use it in GitHub Desktop.
A portable statusbar script which can update its components at different time intervals, using minimal sub-processes
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -ef | |
main() { | |
trap 'kill 0' EXIT HUP INT | |
emitter | collector | |
} | |
# All variables used here are dynamically assigned via `export`. | |
# Silence shellcheck warnings about the use of unassigned variables. | |
# shellcheck disable=2154 | |
update() { | |
fmt='D:%-5s L:%4s C:%3s%% T:%2sC M:%3s%% D:%10s U:%10s %s %s\n' | |
set -- "${desktop:=-}" "$cpu_load" "$cpu_pct" "${cpu_temp%???}" \ | |
"$mem_usage" "$net_down" "$net_up" "$date" "$time" | |
# To stay below 75 columns we use a variable as format string for | |
# `printf`. Disable shellcheck warning about this. | |
# shellcheck disable=2059 | |
printf "$fmt" "$@" | |
} | |
emitter() { | |
every() { delta=${1:-1}; [ $((${round:=0} % delta)) -eq 0 ]; } | |
# Event driven items should be added (and backgrounded) here. | |
[ -n "$DISPLAY" ] && | |
desktops & | |
while :; do | |
# Shortest interval should come last, so that delta | |
# (set in every()) has the right value. | |
every 60 && { | |
cpu_load | |
date_time | |
} | |
every 4 && { | |
cpu_pct | |
cpu_temp | |
mem_usage | |
net down | |
net up | |
} | |
printf update\\n | |
round=$((round + delta)) | |
sleep "$delta" | |
done | |
} | |
collector() { | |
while IFS= read -r line; do | |
case $line in | |
update) update ;; | |
*) | |
# The expansion is intentional here. | |
# shellcheck disable=2163 | |
export "$line" | |
;; | |
esac | |
done | |
} | |
########################################################################## | |
cpu_load() { | |
# We don't make use of load of the last 5 and 15 minutes. | |
# shellcheck disable=2034 | |
read -r cpu_load cpu_load5 cpu_load15 x </proc/loadavg | |
printf 'cpu_load=%s\n' "$cpu_load" | |
} | |
cpu_temp() { | |
read -r cpu_temp </sys/class/thermal/thermal_zone0/temp | |
printf 'cpu_temp=%s\n' "$cpu_temp" | |
} | |
# This function assigns the $prev_* variables using `eval`, silence the | |
# wrong warnings about those variables not being assigned. | |
# shellcheck disable=2154 | |
cpu_pct() { | |
vars="user nice system idle iowait irq softirq steal" | |
# Store previous values. | |
for var in $vars; do | |
eval "prev${var}=\${$var:-0}" | |
done | |
# Read new values. | |
# The expansion and word splitting of $vars is intentional. | |
# shellcheck disable=2229,2086 | |
while read -r name $vars x </proc/stat; do | |
[ "$name" = "cpu" ] && break | |
done | |
# Based on https://stackoverflow.com/a/23376195. | |
PrevIdle=$((previdle + previowait)) | |
Idle=$((idle + iowait)) | |
PrevNonIdle=$((prevuser + prevnice + prevsystem | |
+ previrq + prevsoftirq + prevsteal)) | |
NonIdle=$((user + nice + system + irq + softirq + steal)) | |
PrevTotal=$((PrevIdle + PrevNonIdle)) | |
Total=$((Idle + NonIdle)) | |
# Differentiate: actual value minus the previous one. | |
totald=$((Total - PrevTotal)) | |
idled=$((Idle - PrevIdle)) | |
printf 'cpu_pct=%s\n' "$((100 * (totald - idled) / totald))" | |
} | |
date_time() { | |
date=$(date +'%F %H:%M') | |
printf 'date=%s\ntime=%s\n' "${date% *}" "${date#* }" | |
} | |
desktops() { | |
desk_name_by_num() { shift "$(($1 + 1))"; printf %s\\n "$1"; } | |
xprop -spy -root -notype \ | |
_NET_DESKTOP_NAMES \ | |
_NET_CURRENT_DESKTOP \ | |
| while read -r line; do | |
case $line in | |
_NET_DESKTOP_NAMES*) | |
# Use `tr` instead of parameter expansion, | |
# since this only ever runs once anyway. | |
# Wordsplitting is intentional here. | |
# shellcheck disable=2046 | |
set -- $(printf %s\\n "${line#* = }" \ | |
| tr -d \",) | |
;; | |
*) | |
num=${line#* = } | |
printf 'desktop=%s\nupdate\n' \ | |
"$(desk_name_by_num "$num" "$@")" | |
;; | |
esac | |
done | |
} | |
mem_usage() { | |
unset memtotal memavail | |
# Loop until $memtotal and $memavail are set. | |
# We want to discard the third column so we split it into $x. It | |
# will remain unused, hence silence shellcheck. | |
# shellcheck disable=2034 | |
while read -r name val x; do | |
case $name in | |
MemTotal:) | |
memtotal=$val | |
[ -n "${memavail:-}" ] && break | |
;; | |
MemAvailable:) | |
memavail=$val | |
[ -n "${memtotal:-}" ] && break | |
;; | |
esac | |
done </proc/meminfo | |
printf 'mem_usage=%s\n' \ | |
"$((100 * (memtotal - memavail) / memtotal))" | |
} | |
net() { | |
net_interface=enp3s0 | |
case ${1:-down} in | |
down) | |
f=rx | |
var_name=net_down | |
prev=${rx:-x} | |
;; | |
up) | |
f=tx | |
var_name=net_up | |
prev=${tx:-x} | |
;; | |
esac | |
path=/sys/class/net/$net_interface/statistics/${f}_bytes | |
# We either read "rx" or "tx" bytes. The expansion of $f is thus | |
# intentional. | |
# shellcheck disable=2229 | |
read -r "$f" <"$path" | |
case $f in | |
rx) bytes=$rx ;; | |
tx) bytes=$tx ;; | |
esac | |
case $prev in | |
x) prev=$bytes ;; | |
esac | |
bytes=$(((bytes - prev) / delta)) | |
if [ "${#bytes}" -gt 9 ]; then | |
unit=GB | |
value=${bytes%?????????} | |
elif [ "${#bytes}" -gt 6 ]; then | |
unit=MB | |
value=${bytes%??????} | |
elif [ "${#bytes}" -gt 3 ]; then | |
unit=kB | |
value=${bytes%???} | |
else | |
printf '%s=%d B/s\n' "$var_name" "$bytes" | |
return | |
fi | |
value=$value.${bytes#$value} | |
printf '%s=%.1f %s/s\n' "$var_name" "$value" "$unit" | |
} | |
main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment