Skip to content

Instantly share code, notes, and snippets.

@aaronNGi
Created January 29, 2021 09:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aaronNGi/5aacadb072b356aab261e292a7be30e6 to your computer and use it in GitHub Desktop.
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
#!/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