如何将 while 循环发出的所有行存储在 shell 中的单个变量中?

如何将 while 循环发出的所有行存储在 shell 中的单个变量中?

据我所知,shell中的while循环是在子shell中执行的,因此它们不能修改循环之外的变量。

我正在编写一个 shell 脚本,我想将机器的所有内部 IP 存储到一个变量中,因此使用 for 循环处理该变量,以使用 iptables 对它们进行一一过滤。

我可以写这段代码:

ip route show default | awk '{ print $5 }' | while read line; do
  ip address show dev ${line} scope global | awk '/inet / {sub(/\/.*/, "", $2); print $2}' | while read line; do
    echo "${line} "
  done
done

输出:

10.17.0.49 
192.168.1.4

我的问题是:

如何将 while 循环发出的所有这些行存储到单个变量中(因为 while 循环变量是易失性的)?

答案1

要打印默认路由中涉及的接口的所有全局范围本地地址,我将使用 JSON 格式,该格式可以以更可靠的方式以编程方式处理:

perl -MJSON -le '
  $default_routes = decode_json(qx(ip -j route show default));
  for (@$default_routes) {$devs{$_->{dev}} = 1}
  $addresses = decode_json(qx(ip -j address show));
  for (@$addresses) {
    if ($devs{$_->{ifname}}) {
      for (@{$_->{addr_info}}) {
        print $_->{local} if $_->{scope} eq "global";
      }
    }
  }'

或者同样使用jq

ip -j address show |
  jq -r --argjson devs "$(
      ip -j route show default|jq 'map({"key":.dev})|from_entries'
    )" '.[]|select(.ifname|in($devs)).addr_info[]|
      select(.scope == "global").local'

(它需要相对较新的版本来iproute2支持 JSON 输出)。

要将其(或者更常见的是某个命令输出的每一行)放入数组中bash,请使用:

readarray -t array < <(
  that-command above
)

如果目的是获取通过默认路由发出的数据包将获得的源 IP 地址,请参见示例我的答案如何获取我自己的 IP 地址并将其保存到 shell 脚本中的变量中?

答案2

为了简洁地回答提出的实际问题,无论如何存储 while 循环的输出的上下文:

将输出存储到文件中,例如/path/to/file

while command; do
    thing
done > /path/to/file

将输出存储到变量中,例如my_var

my_var="$(while command; do thing; done)"

答案3

我知道有两种方法可以做到这一点(在bash中):

  1. 使用< <(command)称为进程替换(这不会通过管道将内容传递到循环while)。
  2. 使用{ code or commands }调用的命令分组。

因此,如果您想将所有 IP 存储在变量(或本例中为数组)中,可以使用以下代码:

流程替代:

process_subs () {
   local array=()
   while read -r line; do
     while read -r line; do
       array+=("$line")
     done < <(ip address show dev "${line}" scope global | awk '/inet / {sub(/\/.*/, "", $2); print $2}')
   done < <(ip route show default | awk '{ print $5 }')

   echo Array: "${array[@]}"
   echo Length: ${#array[@]}
}

命令分组:

command_grouping () {

ip route show default | awk '{ print $5 }' |  while read -r line; do
ip address show dev "${line}" scope global | awk '/inet / {sub(/\/.*/, "", $2); print $2}' | { 
         local array=()
            while read -r line; do
            array+=("$line")
         done

      echo Array: "${array[@]}"
      echo Length: ${#array[@]}
      }
      done 
}

相关内容