10

I just wrote the following bash script to check the ping access on list of Linux machines:

for M in $list
 do
   ping -q -c 1  "$M" >/dev/null 
          if [[ $? -eq 0 ]]
   then
    echo "($C) $MACHINE CONNECTION OK"
   else
    echo "($C) $MACHINE CONNECTION FAIL"
   fi

   let C=$C+1
done

This prints:

 (1) linux643 CONNECTION OK
 (2) linux72 CONNECTION OK
 (3) linux862 CONNECTION OK
 (4) linux12 CONNECTION OK
 (5) linux88 CONNECTION OK
 (6) Unix_machinetru64 CONNECTION OK

How can I use printf (or any other command) in my bash script in order to print the following format?

 (1) linux643 ............ CONNECTION OK
 (2) linux72 ............. CONNECTION OK
 (3) linux862 ............ CONNECTION OK
 (4) linux12 ............. CONNECTION OK
 (5) linux88 ............. CONNECTION FAIL
 (6) Unix_machinetru64 ... CONNECTION OK
  • You can do a calculation, $TOTAL (length) - $MASHINE (length) to get the number of dots. Then use printf '.%.s' {1..$DOTS} in each loop iteration. Something like this I think will work. – coffeMug Jun 30 '16 at 14:02
  • can you please described your solution as answer – yael Jun 30 '16 at 14:04
  • You already have an answer. ;-) – coffeMug Jun 30 '16 at 14:08
  • See my answer at StackOverflow – Dennis Williamson Jun 30 '16 at 22:21
15

Using parameter expansion to replace spaces resulting from %-s by dots:

#!/bin/bash
list=(localhost google.com nowhere)
C=1
for M in "${list[@]}"
do
    machine_indented=$(printf '%-20s' "$M")
    machine_indented=${machine_indented// /.}

    if ping -q -c 1  "$M" &>/dev/null ;  then
        printf "(%2d) %s CONNECTION OK\n" "$C" "$machine_indented"
    else
        printf "(%2d) %s CONNECTION FAIL\n" "$C" "$machine_indented"
    fi
    ((C=C+1))
done
  • WOW , let me check and I will update soon .......................... – yael Jun 30 '16 at 14:19
  • 1
    Heh, clever! A couple of pedantic points: i) the %2d is adding a needless space inside the parentheses (although it might be useful when $list >=10); ii) to get the OP's exact output, you might want to add machine_indented=${machine_indented/../ .} to add an extra space before the first .. As I said, pedantic. – terdon Jun 30 '16 at 14:24
  • hi Choroba , can you please consider terdon remarks in your answer ? – yael Jun 30 '16 at 14:33
  • @yael: It should be now easy for you to tweak the solution :-) – choroba Jun 30 '16 at 14:42
  • BTW - why to add & before >/dev/null ? – yael Jun 30 '16 at 14:59
8

for m in $list is zsh syntax. In bash it would be for i in "${list[@]}".

bash doesn't have padding operators. You can do padding with printf but only with spaces, not arbitrary characters. zsh has padding operators.

#! /bin/zsh -
list=(
  linux643
  linux72
  linux862
  linux12
  linux88
  Unix_machinetru64
)
c=0
for machine in $list; do
  if ping -q -c 1 $machine >& /dev/null; then
    state=OK
  else
    state=FAIL
  fi
  printf '%4s %s\n' "($((++c)))" "${(r:25::.:):-$machine } CONNECTION $state"
done

The padding operator is ${(r:25:)parameter} to right-pad with length 25 with spaces or ${(r:25::string:)parameter} to right-pad with any string instead of space.

We also use printf '%4s' to left-pad the (x) with spaces. We could have used ${(l:4:):-"($((++c)))"} instead. A notable difference though is that if the string is more than 4 characters long, ${(l)} would truncate it, while it would overflow with printf.

5

The %s format specifier can take a precision (%.20s for example), and just as when you want to output a float value to a certain precision (with %.4f for example), the output will be at most that many characters from the given string argument.

So create a string that contains the machine name and enough dots to run out of dots:

cnt=0
for hname in vboxhost ntp.stupi.se example.com nonexistant; do
   if ping -q -c 1  "$hname" >/dev/null 2>&1; then
       status="OK"
   else
       status="FAIL"
   fi

   printf "(%d) %.20s CONNECTION %s\n" \
       "$(( ++cnt ))" "$hname ...................." "$status"

done

Output:

(1) vboxhost ........... CONNECTION OK
(2) ntp.stupi.se ....... CONNECTION OK
(3) example.com ........ CONNECTION OK
(4) nonexistant ........ CONNECTION FAIL
2

With stuff stolen from @choroba's answer:

#!/bin/bash 
list=(linux643 linux72 google.com linux862 linux12 linux88 unix_machinetru64) 
C=1 
readonly TOTAL=50 
for M in "${list[@]}" 
do 
    DOTS=$(( TOTAL - ${#M} ))
    ping -q -c 1  "$M" &>/dev/null 

    if (($?)) ;  then 
        printf "(%d) %s" "$C" "$M" ; printf "%0.s." $(seq 1 $DOTS) ; printf " CONNECTION FAILED\n" 
    else 
        printf "(%d) %s" "$C" "$M" ; printf "%0.s." $(seq 1 $DOTS) ; printf " CONNECTION OK\n"  
    fi 
    ((C=C+1)) 
done 
1

I'd do it with fping and awk. Unfortunately, awk's printf can't pad with dots, only with spaces or zeroes so I have to write a function:

list=(kali surya indra ganesh durga hanuman nonexistent)

fping "${list[@]}" 2>&1 | 
  sort -k3 |
  awk -F'[: ]' 'BEGIN { fmt="(%02d) %s CONNECTION %s\n"};

       function dotpad(s,maxlen,     l,c,pads) {
         l = maxlen - length(s);
         pads = "";
         for (c=0;c<l;c++) {pads=pads"."};
         return s " " pads
       };

       /alive$/       { printf fmt, ++i, dotpad($1,19), "OK" };
       /unreachable$/ { printf fmt, ++i, dotpad($1,19), "FAIL" }
       /not known$/   { printf fmt, ++i, dotpad($1,19), "IMPOSSIBLE" } '
(01) durga .............. CONNECTION OK
(02) ganesh ............. CONNECTION OK
(03) indra .............. CONNECTION OK
(04) kali ............... CONNECTION OK
(05) nonexistent ........ CONNECTION IMPOSSIBLE
(06) hanuman ............ CONNECTION FAIL
(07) surya .............. CONNECTION FAIL

I'm using zero-padded 2-digit numbers in the parentheses so that the format doesn't get screwed up if there are 10-99 hosts in $list (100+ will still screw it up). The alternative would be to delay printing until an END {} block, and for the /regexp-matches/ to just insert the hostname into one of three arrays, e.g. ok,fail,unknown. or just one associative array (e.g. hosts[hostname]="OK"). Then you could count the number of lines and use that to decide how wide the line-counter field should be.

I've also decided to have the output distinguish between unknown hosts (CONNECTION IMPOSSIBLE) and unreachable hosts (CONNECTION FAIL).

The sort -k3 is optional, it just groups the output by the fping result ("hostname is alive", "hostname is unreachable" or "hostname: Name or service not known"). Without the sort, the unknown hosts will always appear first in the output. Just plain sort without the -k3 will sort by hostname.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.