Bash + mit printf, um in einem speziellen Format zu drucken

12

Ich habe gerade das folgende Bash-Skript geschrieben, um den Ping-Zugriff auf die Liste der Linux-Computer zu überprüfen:

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

Dies druckt:

 (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

Wie kann ich printf(oder einen anderen Befehl) in meinem Bash-Skript verwenden, um das folgende Format zu drucken?

 (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
quelle
Sie können eine Berechnung durchführen, $TOTAL (length) - $MASHINE (length)um die Anzahl der Punkte zu ermitteln. Verwenden Sie dann printf '.%.s' {1..$DOTS}in jeder Schleifeniteration. So etwas wird meiner Meinung nach funktionieren.
Kaffee Tasse
Können
Sie haben bereits eine Antwort. ;-)
coffeMug
Siehe meine Antwort bei StackOverflow
Bis auf weiteres angehalten.

Antworten:

19

Verwenden der Parametererweiterung zum Ersetzen von Leerzeichen %-sdurch Punkte:

#!/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
quelle
WOW, lass mich
nachsehen
1
Heh, klug! Ein paar pedantische Punkte: i) das %2dfügt einen unnötigen Platz in den Klammern hinzu (obwohl es nützlich sein kann, wenn $ list> = 10 ist); ii) Um die genaue Ausgabe des OP zu erhalten , möchten Sie möglicherweise hinzufügen machine_indented=${machine_indented/../ .}, um vor dem ersten einen zusätzlichen Speicherplatz hinzuzufügen .. Wie gesagt pedantisch.
Terdon
Hallo Choroba, kannst du bitte Terdon Bemerkungen in deiner Antwort berücksichtigen?
Yael
@yael: Es sollte jetzt einfach für Sie sein, die Lösung zu optimieren :-)
Choroba
Übrigens - warum & vor> / dev / null hinzufügen?
Yael
8

for m in $listist zshSyntax. Darin bashwäre for i in "${list[@]}".

bashhat keine Auffülloperatoren. Sie können printfmit Leerzeichen auffüllen, jedoch nur mit Leerzeichen, nicht mit beliebigen Zeichen. zshhat Polsterungsoperatoren.

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

Der padding - Operator ist , ${(r:25:)parameter}um rechte -pad mit der Länge 25 mit Räumen oder ${(r:25::string:)parameter}zur rechten -pad mit jeder Zeichenfolge anstelle des Raumes.

Wir verwenden auch printf '%4s'das linke Pad (x)mit Leerzeichen. Wir hätten ${(l:4:):-"($((++c)))"}stattdessen verwenden können. Ein bemerkenswerter Unterschied ist jedoch, dass wenn die Zeichenfolge länger als 4 Zeichen ist, ${(l)}sie abgeschnitten wird, während sie mit überläuft printf.

Stéphane Chazelas
quelle
6

Der %sFormatbezeichner kann eine Genauigkeit annehmen ( %.20szum Beispiel), und genau wie wenn Sie einen Gleitkommawert mit einer bestimmten Genauigkeit ausgeben möchten (mit %.4fzum Beispiel), wird die Ausgabe höchstens so viele Zeichen aus dem angegebenen Zeichenfolgenargument enthalten.

Erstellen Sie also eine Zeichenfolge, die den Maschinennamen und genügend Punkte enthält, damit die Punkte ausgehen:

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

Ausgabe:

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

Mit Sachen, die aus @ chorobas Antwort gestohlen wurden:

#!/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 
Kaffeetasse
quelle
2

Ich würde es mit fpingund machen awk. Leider awkist printfkann nicht Pad mit Punkten, nur mit Leerzeichen oder Nullen so habe ich eine Funktion schreiben:

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

Ich verwende nullgefüllte zweistellige Zahlen in Klammern, damit das Format nicht durcheinander gerät, wenn 10-99 Hosts vorhanden sind $list(100+ vermasseln es immer noch). Die Alternative wäre, zu verzögern , bis ein Druck - END {}Block und für das / regexp-matches / Insert nur den Host - Namen in einem von drei Gruppen, zum Beispiel ok, fail, unknown. oder nur ein assoziatives Array (zB hosts[hostname]="OK"). Dann können Sie die Anzahl der Zeilen zählen und damit entscheiden, wie breit das Zeilenzählerfeld sein soll.

Ich habe auch beschlossen, die Ausgabe zwischen unbekannten Hosts ( CONNECTION IMPOSSIBLE) und nicht erreichbaren Hosts ( CONNECTION FAIL) unterscheiden zu lassen.

Das sort -k3ist optional, es gruppiert nur die Ausgabe nach dem fpingErgebnis ("Hostname lebt", "Hostname ist nicht erreichbar" oder "Hostname: Name oder Dienst nicht bekannt"). Ohne das sortwerden die unbekannten Hosts immer zuerst in der Ausgabe angezeigt. Einfach sortohne den -k3Willen nach Hostnamen sortieren.

cas
quelle