#!/bin/bash # quickGetPing_OldBash - How to collect many ping's outputs into bash variables # !! This is an alternate version suitable for old bash V4.2.53(2)-release !! # (C) 2025 Felix Hauri - felix@f-hauri.ch # Licensed under terms of GPL v3. www.gnu.org # This script is intented to be used under GNU/Linux, as they use kernel # pseudo filesystem /proc (only in `getArps` function). # Collecting networks IPv4 status and infos from parallelized ping's answer, # into three differents bash arrays: # - $listHosts Associative array: ([hosta]='10.2.3.45' [hostb]='10.2.3.46'... # - $byIp Indexed array: ([167904045]='hosta' [167904046]='hostb'... # - $stateList Integer idexed array: ([167904045]='1' [167904046]='0'... # Note: In order to avoid bash loops, all array operation are done by "mapping" # ( While keeping in mind `declare -a "$str"` is like an `eval`!! ) # In a second operation, variable "$listArps" are built with command: # mapfile [ip or hostname] [ip or hostname]... # Something like: quickGetPing $(awk '/^[1.9]/{if($3)print $3}' > 24 }") printf -v __tmp_str "$__tmp_str" ${__tmp_int[@]} __tmp_int=("${@/%/ >> 16 & 255}") printf -v __tmp_str "$__tmp_str" ${__tmp_int[@]} __tmp_int=("${@/%/ >> 8 & 255}") printf -v __tmp_str "$__tmp_str" ${__tmp_int[@]} __tmp_int=("${@/%/ & 255}") printf -v __tmp_str "$__tmp_str" ${__tmp_int[@]} if [[ $__out_var ]]; then mapfile -t "$__out_var" <<<"${__tmp_str%$'\n'}" else echo "${__tmp_str//$'\n'/ }" fi } ip2int() { # Convert array of IPv4 to array of integer, by mapping if [[ $1 == -v ]]; then local __out_var="$2" shift 2 else local __out_var= fi set -- "${@/./<<24|}"; set -- "${@/./<<16|}"; set -- "${@/./<<8|}" local -ai res; res=("$@") if [[ $__out_var ]]; then #mapfile -d\ -t "$__out_var" <<<"${res[*]}" declare -gai "$__out_var=(${res[*]})" else echo "${res[*]}" fi } checkIPv4() { # Work in progress, not used in this script. local __args_array=("$@") __tmp_str printf -v __tmp_str '[%s]= ' ${__args_array[*]//[0-9]} local -A __check_Aarray="($__tmp_str)" (( ${#__check_Aarray[@]} == 1 )) && [[ ${!__check_Aarray[*]} == ... ]] || return 1; __args_array=(${__args_array[*]//[12][0-9][0-9]/.}) __args_array=(${__args_array[*]//[0-9][0-9]/.}) printf -v __tmp_str '[%s]= ' ${__args_array[*]//[0-9]/.} local -A __check_Aarray="($__tmp_str)" (( ${#__check_Aarray[@]} == 1 ))&& [[ ${!__check_Aarray[*]} == ....... ]]|| return 1 printf -v __tmp_str ' %d > 255 || ' ${*//./ } return $(($__tmp_str 0)) } runPPing () { # This will create 3 array variables by using SDTOUT and 2 more FDs in # backgrounded task (sed) who will send separatedly, required output # to specific FD. local -i hstFd byIpFd local str exec {hstFd}<> <(:); exec {byIpFd}<> <(:); local -ai upList="($( sed -une ' /PING \(.*\) (\([0-9.]\+\)) 56.*/{ s//[\1]=\2 /; w /dev/fd/'$hstFd -e ' /\[\(.*\)\]=\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\) $/{ s//[\2<<24|\3<<16|\4<<8|\5]="\1" /;w /dev/fd/'$byIpFd -e ' }; }; /^64.*(\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)):.*/{ s//[\1<<24|\2<<16|\3<<8|\4]=1 /p }; /^64 .* from \([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)\.\([0-9]\+\)[^)]*/{ s//[\1<<24|\2<<16|\3<<8|\4]=1 /p }; ' < <( for hst; do # in ${hosts[@]}; do ping -c$numPing -W1 $hst & done | cat )))" printf '\1' >&$hstFd read -d$'\1' -u $hstFd str exec {hstFd}>&- declare -gA "listHosts=(${str//$'\n'})" printf '\1' >&$byIpFd read -d$'\1' -u $byIpFd str exec {byIpFd}>&- declare -ga "byIp=(${str//$'\n'})" printf -v str '[%s]=0 ' ${!byIp[@]} declare -gai "stateList=($str)" printf -v str '[%d]+=1 ' ${!upList[@]} declare -gai "stateList+=($str)" } getArps() { local arpList arpCleanedList str mapfile -t arpList &2 "Arguments missing!" sed -ne "/^# Usage/,+1{s|quickGetPing|$0|;s/^# //;p}" "$0" >&2 exit 1 fi # started=$EPOCHREALTIME coproc DATE { stdbuf -o0 date -f - +%s%N;} echo now >&${DATE[1]} read -ru ${DATE[0]} started runPPing "$@" getArps ### Built variables could be show by uncommenting next line. # declare -p listHosts byIp stateList listArps $showArrays && declare -p listHosts byIp stateList listArps msgs=(Down Up) allInts=(${!byIp[@]}) int2ip -v sortedIp ${allInts[@]} colors=( "${stateList[@]/0/2}" ) _tmpArry=("${stateList[@]/#/\${msgs[}") _tmpArry=("${_tmpArry[@]/%/]\}}") # declare -a states="(${stateList[*]/*/\${msgs[&]\}})" declare -a states="(${_tmpArry[*]/\\})" #declare -a macaddrs="(${allInts[*]/*/\"\$\{listArps[&]\}\"})" _tmpArry=("${allInts[@]/#/\"\${listArps[}") _tmpArry=("${_tmpArry[@]/%/]\}\"}") declare -a macaddrs="(${_tmpArry[*]/\\})" str='\e[%%%%%%%%%%%%%%%%sm%-24s %%-17s%%%%%%%%-19s%%%%s\e[0m\n' printf -v str "$str" Name "${byIp[@]}" printf -v str "$str" IP\ Address "${sortedIp[@]}" printf -v str "$str" State "${states[@]}" printf -v str "$str" 'Mac Address' "${macaddrs[@]}" printf "$str" 4 "${colors[@]/1/0}" # echo "Same, but using a loop:" # printf '\e[4m%-24s %-17s%-19s%s\e[0m\n' Name IP\ Address State Arps # colors=(2 0) # for int in ${!byIp[@]}; do # printf '\e[%sm%-24s %-17s%-19s%s\e[0m\n' \ # ${colors[stateList[int]]} ${byIp[int]} \ # ${listHosts[${byIp[int]}]} \ # "${listArps[int]}" ${msgs[stateList[int]]} # done # ended=$EPOCHREALTIME echo now >&${DATE[1]} read -ru ${DATE[0]} ended elap=00000000$(( ${ended/.} - ${started/.} )) printf 'Processed: %d hosts in %.3f seconds.\n' \ ${#byIp[@]} ${elap::-9}.${elap: -9}