14

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
4
  • 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.
    – Vombat
    Jun 30, 2016 at 14:02
  • can you please described your solution as answer
    – yael
    Jun 30, 2016 at 14:04
  • You already have an answer. ;-)
    – Vombat
    Jun 30, 2016 at 14:08
  • See my answer at StackOverflow Jun 30, 2016 at 22:21

5 Answers 5

21

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
7
  • WOW , let me check and I will update soon ..........................
    – yael
    Jun 30, 2016 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, 2016 at 14:24
  • hi Choroba , can you please consider terdon remarks in your answer ?
    – yael
    Jun 30, 2016 at 14:33
  • @yael: It should be now easy for you to tweak the solution :-)
    – choroba
    Jun 30, 2016 at 14:42
  • BTW - why to add & before >/dev/null ?
    – yael
    Jun 30, 2016 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.

7

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 
2

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.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .