bash + 使用 printf 以特殊格式打印

bash + 使用 printf 以特殊格式打印

我刚刚编写了以下 bash 脚本来检查 Linux 计算机列表上的 ping 访问:

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

这打印:

 (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

如何printf在 bash 脚本中使用(或任何其他命令)来打印以下格式?

 (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

答案1

使用参数扩展来替换由%-s点产生的空格:

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

答案2

for m in $listzsh语法。就在bash其中for i in "${list[@]}"

bash没有填充运算符。您可以使用空格进行填充printf,但只能使用空格,而不能使用任意字符。zsh有填充运算符。

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

填充运算符${(r:25:)parameter}正确的-pad长度为25,带有空格${(r:25::string:)parameter}正确的- 用任何字符串而不是空格填充。

我们也printf '%4s'用来左边- 用空格填充(x)。我们本来可以用它来${(l:4:):-"($((++c)))"}代替。但一个显着的区别是,如果字符串长度超过 4 个字符,${(l)}则会将其截断,而会溢出printf.

答案3

格式%s说明符可以采用精度(%.20s例如),就像当您想要将浮点值输出到特定精度时(例如%.4f)一样,输出最多将是给定字符串参数中的多个字符。

因此,创建一个包含机器名称和足够的点以用完点的字符串:

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

输出:

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

答案4

fping我会用和来做awk。不幸的是,awk'printf不能用点填充,只能用空格或零填充,所以我必须编写一个函数:

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

我在括号中使用零填充的 2 位数字,这样如果有 10-99 个主机$list(100+ 仍然会搞砸),格式就不会搞砸。另一种方法是延迟打印直到出现一个END {}块,并且 /regexp-matches/ 仅将主机名插入到三个数组之一中,例如ok, fail, unknown。或者只是一个关联数组(例如hosts[hostname]="OK")。然后,您可以计算行数并使用它来决定行计数器字段的宽度。

我还决定让输出区分未知主机 ( CONNECTION IMPOSSIBLE) 和无法访问的主机 ( CONNECTION FAIL)。

sort -k3可选的,它只是按结果对输出进行分组fping(“主机名处于活动状态”、“主机名无法访问”或“主机名:名称或服务未知”)。如果没有sort,未知主机将始终首先出现在输出中。只是简单地sort没有-k3将按主机名排序。

相关内容