bash + menggunakan printf untuk mencetak dalam format khusus

12

Saya baru saja menulis skrip bash berikut untuk memeriksa akses ping pada daftar mesin 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

Ini mencetak:

 (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

Bagaimana saya bisa menggunakan printf(atau perintah lain) dalam skrip bash saya untuk mencetak format berikut?

 (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
sumber
Anda bisa melakukan perhitungan, $TOTAL (length) - $MASHINE (length)untuk mendapatkan jumlah titik. Kemudian gunakan printf '.%.s' {1..$DOTS}di setiap iterasi loop. Sesuatu seperti ini saya pikir akan berhasil.
coffeMug
dapatkah Anda menjelaskan solusi Anda sebagai jawaban
yael
Anda sudah punya jawaban. ;-)
coffeMug
Lihat jawaban saya di StackOverflow
Dijeda sampai pemberitahuan lebih lanjut.

Jawaban:

19

Menggunakan ekspansi parameter untuk mengganti spasi yang dihasilkan %-soleh titik:

#!/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
sumber
WOW, izinkan saya memeriksa dan saya akan segera memperbarui ..........................
yael
1
Heh, pintar! Beberapa poin penting: i) %2dmenambahkan ruang yang tidak perlu di dalam tanda kurung (walaupun mungkin berguna ketika $ list> = 10); ii) untuk mendapatkan output OP yang tepat , Anda mungkin ingin menambahkan machine_indented=${machine_indented/../ .}untuk menambah ruang sebelum yang pertama .. Seperti yang saya katakan, pedantic.
terdon
hai Choroba, bisakah Anda mempertimbangkan komentar terdon dalam jawaban Anda?
yael
@yael: Seharusnya sekarang mudah bagi Anda untuk mengubah solusi :-)
choroba
BTW - mengapa menambahkan & sebelum> / dev / null?
yael
8

for m in $listadalah zshsintaks. Di bashdalamnya akan for i in "${list[@]}".

bashtidak memiliki operator padding. Anda dapat melakukan padding dengan printftetapi hanya dengan spasi, bukan karakter yang arbitrer. zshmemiliki operator padding.

#! /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 adalah ${(r:25:)parameter}untuk benar -pad dengan panjang 25 dengan spasi atau ${(r:25::string:)parameter}ke kanan -pad dengan string bukannya ruang.

Kami juga menggunakan printf '%4s'untuk -pad kiri(x) dengan spasi. Kita bisa menggunakannya ${(l:4:):-"($((++c)))"}sebagai gantinya. Perbedaan yang mencolok adalah bahwa jika string lebih dari 4 karakter, ${(l)}akan memotongnya, sementara itu akan meluap bersama printf.

Stéphane Chazelas
sumber
6

Penentu %sformat dapat mengambil presisi ( %.20smisalnya), dan sama seperti ketika Anda ingin menampilkan nilai float ke presisi tertentu (dengan %.4fmisalnya), output akan paling banyak karakter dari argumen string yang diberikan.

Jadi buat string yang berisi nama mesin dan cukup titik untuk kehabisan titik:

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

Keluaran:

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

Dengan barang-barang yang dicuri dari jawaban @ 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
sumber
2

Saya akan melakukannya dengan fpingdan awk. Sayangnya, awkitu printftidak bisa pad dengan titik, hanya dengan spasi atau nol jadi saya harus menulis fungsi:

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

Saya menggunakan angka 2 digit nol-empuk di dalam tanda kurung sehingga format tidak kacau jika ada 10-99 host di $list(100+ masih akan mengacaukannya). Alternatif akan menunda pencetakan sampai seorang END {}blok, dan untuk / regexp-pertandingan / hanya memasukkan nama host ke dalam satu dari tiga array, misalnya ok, fail, unknown. atau hanya satu array asosiatif (misalnya hosts[hostname]="OK"). Kemudian Anda bisa menghitung jumlah garis dan menggunakannya untuk memutuskan seberapa lebar bidang penghitung garis seharusnya.

Saya juga memutuskan untuk membuat keluaran membedakan antara host yang tidak dikenal ( CONNECTION IMPOSSIBLE) dan host yang tidak terjangkau ( CONNECTION FAIL).

The sort -k3adalah opsional, itu hanya kelompok output dengan fpinghasil ( "hostname masih hidup", "hostname adalah unreachable" atau "hostname: Nama atau layanan yang tidak dikenal"). Tanpa itu sort, host yang tidak dikenal akan selalu muncul pertama kali di output. Cukup sorttanpa -k3kehendak untuk mengurutkan berdasarkan nama host.

cas
sumber