bash + usando printf para imprimir em formato especial

12

Acabei de escrever o seguinte script bash para verificar o acesso ao ping na 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

Isso 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

Como posso usar printf(ou qualquer outro comando) no meu script bash para imprimir o seguinte 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
fonte
Você pode fazer um cálculo $TOTAL (length) - $MASHINE (length)para obter o número de pontos. Em seguida, use printf '.%.s' {1..$DOTS}em cada iteração de loop. Algo assim acho que vai funcionar.
coffemug
você pode, por favor, descreveu sua solução como resposta
yael
Você já tem uma resposta. ;-)
coffemug
Veja minha resposta no StackOverflow
Pausado até novo aviso.

Respostas:

19

Usando expansão de parâmetro para substituir espaços resultantes de %-spontos:

#!/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
fonte
WOW, deixe-me ver e eu vou atualizar em breve ..........................
yael
1
Heh, esperto! Alguns pontos pedantes: i) %2destá adicionando um espaço desnecessário entre parênteses (embora possa ser útil quando $ list> = 10); ii) para obter a saída exata do OP , machine_indented=${machine_indented/../ .}adicione um espaço extra antes da primeira .. Como eu disse, pedante.
terdon
oi Choroba, você pode considerar as observações de terdon em sua resposta?
yael
@yael: Deve ser agora mais fácil para você ajustar a solução :-)
choroba
BTW - por que adicionar & antes> / dev / null?
yael
8

for m in $listé zshsintaxe. Dentro bashseria for i in "${list[@]}".

bashnão possui operadores de preenchimento. Você pode preencher com printfapenas espaços, mas não com caracteres arbitrários. zshpossui operadores de preenchimento.

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

O operador de preenchimento deve ${(r:25:)parameter}colocar o teclado à direita com comprimento 25, com espaços, ou pressionar o teclado ${(r:25::string:)parameter}à direita com qualquer string em vez de espaço.

Nós também usamos printf '%4s'a esquerda -pad o (x)com espaços. Poderíamos ter usado ${(l:4:):-"($((++c)))"}. Uma diferença notável, porém, é que, se a string tiver mais de 4 caracteres, ela ${(l)}será truncada, enquanto ela transbordará printf.

Stéphane Chazelas
fonte
6

O %sespecificador de formato pode ter uma precisão ( %.20spor exemplo) e, assim como quando você deseja gerar um valor flutuante para uma certa precisão (com, %.4fpor exemplo), a saída terá no máximo muitos caracteres do argumento de string especificado.

Portanto, crie uma string que contenha o nome da máquina e pontos suficientes para ficar sem pontos:

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

Resultado:

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

Com coisas roubadas da resposta da @ 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
fonte
2

Eu faria isso com fpinge awk. Infelizmente, awknão é printfpossível preencher com pontos, apenas com espaços ou zeros, então eu tenho que escrever uma função:

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

Estou usando números de 2 dígitos preenchidos com zero entre parênteses, para que o formato não seja estragado se houver 10-99 hosts $list(mais de 100 ainda estragam tudo). A alternativa seria a atrasar a impressão até que um END {}bloco de, e para as partidas / EXPREG-/ a inserção apenas o nome da máquina em uma das três matrizes, por exemplo ok, fail, unknown. ou apenas uma matriz associativa (por exemplo hosts[hostname]="OK"). Depois, você pode contar o número de linhas e usá-lo para decidir a largura do campo do contador de linhas.

Também decidi fazer a saída distinguir entre hosts desconhecidos ( CONNECTION IMPOSSIBLE) e hosts inacessíveis ( CONNECTION FAIL).

A sort -k3é opcional, apenas grupos a saída pelo fpingresultado ( "hostname está vivo", "hostname é inacessível" ou "hostname: Nome ou serviço desconhecido"). Sem o sort, os hosts desconhecidos sempre aparecerão primeiro na saída. Simplesmente sortsem a -k3vontade ordenar por nome de host.

cas
fonte