bash + en utilisant printf pour imprimer dans un format spécial

12

Je viens d'écrire le script bash suivant pour vérifier l'accès ping sur la liste des machines Linux:

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

Cela imprime:

 (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

Comment puis-je utiliser printf(ou toute autre commande) dans mon script bash afin d'imprimer le format suivant?

 (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
Yael
la source
Vous pouvez faire un calcul, $TOTAL (length) - $MASHINE (length)pour obtenir le nombre de points. Ensuite, utilisez printf '.%.s' {1..$DOTS}dans chaque itération de boucle. Je pense que quelque chose comme ça fonctionnera.
coffeMug
pouvez-vous s'il vous plaît décrire votre solution comme une réponse
yael
Vous avez déjà une réponse. ;-)
coffeMug
Voir ma réponse sur StackOverflow
pause jusqu'à nouvel ordre.

Réponses:

19

Utiliser l'expansion des paramètres pour remplacer les espaces résultant de %-spoints:

#!/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
choroba
la source
WOW, laissez-moi vérifier et je
mettrai à
1
Hé, intelligent! Quelques points pédantes: i) l' %2dajout d'un espace inutile à l'intérieur des parenthèses (bien que cela puisse être utile lorsque $ list> = 10); ii) pour obtenir la sortie exacte de l'OP , vous voudrez peut-être ajouter machine_indented=${machine_indented/../ .}pour ajouter un espace supplémentaire avant le premier .. Comme je l'ai dit, pédant.
terdon
salut Choroba, pouvez-vous s'il vous plaît tenir compte des remarques de Terdon dans votre réponse?
yael
@yael: Il devrait maintenant être facile pour vous de modifier la solution :-)
choroba
BTW - pourquoi ajouter & avant> / dev / null?
yael
8

for m in $listest la zshsyntaxe. Ce bashserait for i in "${list[@]}".

bashn'a pas d'opérateurs de remplissage. Vous pouvez faire du remplissage avec printfmais uniquement avec des espaces, pas des caractères arbitraires. zsha des opérateurs de remplissage.

#! /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

L' opérateur de remplissage est ${(r:25:)parameter}à droite- pad avec une longueur de 25 avec des espaces ou ${(r:25::string:)parameter}à droite -pad avec n'importe quelle chaîne au lieu de l'espace.

Nous utilisons également printf '%4s'à gauche -pad les (x)espaces. Nous aurions pu utiliser à la ${(l:4:):-"($((++c)))"}place. Une différence notable est cependant que si la chaîne fait plus de 4 caractères, la ${(l)}tronquerait, alors qu'elle déborderait printf.

Stéphane Chazelas
la source
6

Le %sspécificateur de format peut prendre une précision ( %.20spar exemple), et tout comme lorsque vous voulez sortir une valeur flottante avec une certaine précision (avec %.4fpar exemple), la sortie sera au plus autant de caractères de l'argument de chaîne donné.

Créez donc une chaîne contenant le nom de la machine et suffisamment de points pour manquer de points:

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

Production:

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

Avec des trucs volés à la réponse de @ choroba:

#!/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 
coffeMug
la source
2

Je le ferais avec fpinget awk. Malheureusement, awk« s printfne peut pas pad avec des points, uniquement avec des espaces ou des zéros si je dois écrire une fonction:

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

J'utilise des nombres à deux chiffres remplis de zéros entre parenthèses afin que le format ne soit pas vissé s'il y a 10 à 99 hôtes $list(100+ le verront toujours). L'alternative serait de retarder l' impression jusqu'à ce qu'un END {}bloc, et pour les / regexp-matchs / juste insérer le nom d' hôte dans l' un des trois tableaux, par exemple ok, fail, unknown. ou juste un tableau associatif (par exemple hosts[hostname]="OK"). Ensuite, vous pouvez compter le nombre de lignes et l'utiliser pour décider de la largeur du champ du compteur de lignes.

J'ai également décidé de faire la distinction entre les hôtes inconnus ( CONNECTION IMPOSSIBLE) et les hôtes inaccessibles ( CONNECTION FAIL).

Le sort -k3est facultatif, il regroupe simplement la sortie par le fpingrésultat ("le nom d'hôte est vivant", "le nom d'hôte est inaccessible" ou "nom d'hôte: nom ou service inconnu"). Sans le sort, les hôtes inconnus apparaîtront toujours en premier dans la sortie. Tout simplement sortsans la -k3volonté de trier par nom d'hôte.

cas
la source