bash + usando printf para imprimir en formato especial

12

Acabo de escribir el siguiente script bash para verificar el acceso de ping en la lista de máquinas 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

Esto 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

¿Cómo puedo usar printf(o cualquier otro comando) en mi script bash para imprimir el siguiente formato?

 (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
fuente
Puedes hacer un cálculo $TOTAL (length) - $MASHINE (length)para obtener el número de puntos. Luego, use printf '.%.s' {1..$DOTS}en cada iteración de bucle. Algo así creo que funcionará.
coffeMug
¿podría describir su solución como respuesta?
yael
Ya tienes una respuesta. ;-)
coffeMug
Vea mi respuesta en StackOverflow
pausa hasta nuevo aviso.

Respuestas:

19

Usando la expansión de parámetros para reemplazar espacios resultantes de %-spuntos:

#!/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
fuente
WOW, déjame comprobar y actualizaré pronto ..........................
yael
1
Je, listo! Un par de puntos pedantes: i) %2destá agregando un espacio innecesario dentro de los paréntesis (aunque podría ser útil cuando $ list> = 10); ii) para obtener la salida exacta del OP , es posible que desee agregar machine_indented=${machine_indented/../ .}para agregar un espacio adicional antes del primero .. Como dije, pedante.
terdon
hola Choroba, ¿puedes considerar los comentarios de Terdon en tu respuesta?
yael
@yael: Ahora debería ser fácil para usted ajustar la solución :-)
choroba
Por cierto, ¿por qué agregar y antes> / dev / null?
yael
8

for m in $listes la zshsintaxis En basheso estaría for i in "${list[@]}".

bashno tiene operadores de relleno. Puede rellenar printfpero solo con espacios, no con caracteres arbitrarios. zshtiene operadores de relleno.

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

El relleno de operador está ${(r:25:)parameter}a la derecha -pad con una longitud de 25 o con espacios ${(r:25::string:)parameter}a la derecha -pad con cualquier cadena en lugar de espacio.

También usamos printf '%4s'para dejar- pad (x)con espacios. Podríamos haber usado en su ${(l:4:):-"($((++c)))"}lugar. Sin embargo, una diferencia notable es que si la cadena tiene más de 4 caracteres, la ${(l)}truncaría y se desbordaría printf.

Stéphane Chazelas
fuente
6

El %sespecificador de formato puede tomar una precisión ( %.20spor ejemplo), y al igual que cuando desea generar un valor flotante con una precisión determinada (con, %.4fpor ejemplo), la salida tendrá como máximo esa cantidad de caracteres del argumento de cadena dado.

Así que cree una cadena que contenga el nombre de la máquina y suficientes puntos para quedarse sin puntos:

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

Salida:

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

Con cosas robadas de la respuesta 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 
taza de cafe
fuente
2

Lo haría con fpingy awk. Por desgracia, awk's printfno se puede rellenar con puntos, sólo que con espacios o ceros así que tengo que escribir una función:

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

Estoy usando números de 2 dígitos rellenados con ceros entre paréntesis para que el formato no se arruine si hay 10-99 hosts $list(100+ aún lo arruinarán). La alternativa sería retrasar la impresión hasta que un END {}bloque, y para los / de expresiones regulares-partidos / a sólo tiene que insertar el nombre de host en una de tres arrays, por ejemplo ok, fail, unknown. o solo una matriz asociativa (por ejemplo hosts[hostname]="OK"). Luego podría contar el número de líneas y usarlo para decidir qué tan ancho debe ser el campo del contador de línea.

También he decidido que la salida distinga entre hosts desconocidos ( CONNECTION IMPOSSIBLE) y hosts inalcanzables ( CONNECTION FAIL).

El sort -k3es opcional, solo agrupa la salida por el fpingresultado ("el nombre de host está vivo", "el nombre de host es inalcanzable" o "nombre de host: nombre o servicio desconocido"). Sin el sort, los hosts desconocidos siempre aparecerán primero en la salida. Simplemente sortsin la -k3voluntad ordenar por nombre de host.

cas
fuente