#!/bin/bash # 'getCurrentStat bash for sh' simultaneously on many target. # Sample of distributing POSIX shell commands through many hosts, using ssh # then get/retrieve all outputs, format and print them using bash V5+ # More complex, but more robust than 'getCurrentStat to sort' alternative # (C) 2023-2024 Felix Hauri - felix@f-hauri.ch # Licensed under terms of GPL v3. www.gnu.org ## Note: Host list is ignored if arguments (list of user@hosts) are submited!! ## To use an external file replace the following by: exec {hostList} [varname] local p=000$(( ${1}00000/$2 )) # shellcheck disable=SC2059 disable=SC2086 printf ${3+-v} $3 %.2f ${p::-3}.${p: -3} } textSize(){ # usage: textSize [varname] local i=$(( $1 >= 1<<50 ? 5 : $1 >= 1<<40 ? 4 : $1 >= 1<<30 ? 3 : $1 >= 1<<20 ? 2 : $1 > 1023 ? 1 : 0 )) a=(K M G T P) # shellcheck disable=SC1105 disable=SC2210 disable=SC2035 (( i > 4 ? i+=-2 : 0 )) && a=( "${a[@]:2}" ) && set -- $(( $1>>20 )) "$2" local r=00$(( 1000 * $1 / ( 1024**i ) )) # shellcheck disable=SC2059 disable=SC2086 printf -v $2 %.2f%s ${r::-3}.${r: -3} ${a[i]} } collectCpuMemDiskStat() { # Collect infos and format print them export LC_ALL=C # shellcheck disable=SC2046 echo "$1" $( # order position , host name hostname ) $( # uptime, idle time cat /proc/uptime ) $( # mem total, mem used, swap total, swap used free | sed '1d;s/^.*: *\([[:digit:]]\+\) \+\([[:digit:]]\+\) .*/\1 \2/' ) $( # disk total, disk used df -PkT | awk ' BEGIN{ tot=0;use=0;}; END{ print tot,use;}; / ext| xfs| reiser| ntfs| vfat| fat| btrfs/{ tot+=$3 ; use+=$4; };' ) $( # number of cpus * cores grep -c processor /proc/cpuinfo ) $( # model of rapsberry if raspberry or else of CPU if [ -d "/sys/firmware/devicetree/base" ]; then # It's a Raspberry Pi tr \\0 \\n < /sys/firmware/devicetree/base/model else sed -e '/^model name/{s/.*: *//;s/([^)]*)//g;q;};d' >(:) # open This File descriptor, but not linked to ssh # This method is reliable as long expected output stay small. # For bigger ouput, consider using a file, even through compression! ssh "$host" sh < <( declare -f collectCpuMemDiskStat printf 'collectCpuMemDiskStat %q %q\n' "$order" "$target" ) >&$thisFd & allFDs[$!]=$thisFd targets[$!]=${target#*.*.} text[$!]=${string} } declare -i order allFDs pid amtot=0 adtot=0 \ mtot muse stot suse dtot duse ncpu start=${EPOCHREALTIME/.} end if (($#)); then for host; do run1ssh "$host"{,} done else while read -ru $hostList host desc; do case $host in \#*|'' ) ;; * ) run1ssh "$host" "$desc" ;; esac done fi printf '%-13s %-35s %7s %7s %7s %7s %7s %7s %8s %s\n' \ Host Model Mem Disk CPU Mem Swap Disk ReqTime Description while (( ${#allFDs[@]} )); do printf >&2 '\rCurrent run %d: \47%s\47\e[K' ${#allFDs[@]} "${targets[*]}" wait -n -p pid "${!allFDs[@]}" if ((pid)); then read -ru ${allFDs[pid]} \ order host upt idl mtot muse stot suse dtot duse ncpu model end=${EPOCHREALTIME/.} unset "allFDs[pid]" "targets[pid]" elap=00000$(( end - start )) percent $(( ${upt/.} * ncpu - ${idl/.} )) $(( ${upt/.} * ncpu )) pcpu percent $muse $mtot pmem percent $suse $stot pswap percent $duse $dtot pdsk textSize $mtot hmem textSize $dtot hdsk amtot+=mtot adtot+=dtot # shellcheck disable=SC2154 printf -v out[$order] \ '%-13s %-35s %7s %7s %6.2f%% %6.2f%% %6.2f%% %6.2f%% %7.4fs %s' \ "$host" "${model::35}" "$hmem" "$hdsk" "$pcpu" "$pmem" "$pswap" \ "$pdsk" "${elap::-6}.${elap: -6}" "${text[pid]}" fi done end=${EPOCHREALTIME/.} elap=00000$(( end - start )) printf >&2 '\r\e[K' printf "%s\n" "${out[@]}" textSize $amtot hmem textSize $adtot hdsk printf '%-49s%8s%8s%40.4fs\n' Total "$hmem" "$hdsk" "${elap::-6}.${elap: -6}"