如何在每次循环迭代中将命令的输出分配给不同的变量?

如何在每次循环迭代中将命令的输出分配给不同的变量?

我有一个类似这样的脚本:

for chain in http https ssh
do
 iptables -nvxL $chain | tail -1 | awk '{print $2}'
done

但我真正想做的是将iptables每次迭代的命令输出捕获到不同的变量中,其姓名应等于 的当前值chain

因此,对于第一次循环迭代(其中$chainhttp),我希望发生这种情况:

http=$(iptables -nvxL http | tail -1 | awk '{print $2}')

然后接下来我想要这个:

https=$(iptables -nvxL https | tail -1 | awk '{print $2}')

希望您明白这个想法,但不知道如何做到这一点。

答案1

在你的情况下,我将使用关联数组:

declare -A rules

for chain in http https ssh
do
   rules[$chain]=$(iptables -nvxL $chain | tail -1 | awk '{print $2}')
done

然后,您可以通过取消引用来访问输出,如下所示

printf -- "%s\n" "${rules['http']}"

或者

for chain in http https ssh
do
    printf -- "%s\n" "${rules[$chain]}"
done

答案2

关联数组的答案几乎肯定是您想要的方式。

但是,如果您确实想要名为httphttps和的变量ssh(您将使用$http$https和 $引用ssh),那么您可以使用printf -v将命令输出分配给名称包含在 中的变量$chain

for chain in http https ssh; do
    printf -v "$chain" '%s' "$(iptables -nvxL "$chain" | tail -1 | awk '{print $2}')"
done

另请注意,根据这个答案,您可以使用typesetdeclare来实现相同的目的:

for chain in http https ssh; do
    typeset "$chain"="$(iptables -nvxL "$chain" | tail -1 | awk '{print $2}')"
done

没有eval人在做出这个答案的过程中受到伤害。另外,如果您想像 @penguin359 的答案一样从循环中打印变量值,您可以使用bash 变量间接取消引用变量名 - 再次,不需要evals:

for chain in http https ssh; do
    echo ${!chain}
done

答案3

更新:我忘了引用$2示例中嵌入的内容。它现在应该按照 OP 的要求工作。另外,这个答案背后的要点和使用eval是提供一个跨多个不同实现使用标准 Bourne shell 语法的可移植示例。我已经在 bash、dash 和 zsh 上测试了以下所有代码。 Dash 是对可移植性的更好测试,因为它使用更有限且更符合 Bourne shell 语法的语法。它也是 Debian/Ubuntu 上的默认 shell /bin/sh。达世币中不存在typeset和。printf -v我确实认为eval它很难看,但它是 Bourne shell 中的第一个机制,也是实现真正可移植性的最佳方法。

虽然我认为关联数组是可行的方法,但我想为他最初的问题提供一个准确的答案,并且可以移植到除 Bash 之外的其他 Bourne shell。这就是传统上动态变量名称的完成方式:

for chain in http https ssh; do
    eval $chain="\"\$(iptables -nvxL $chain | tail -1 | awk '{print \$2}')\""
done

Bourne shell 中的运算eval符用于将变量替换为命令,然后在完成后将其作为命令重新运行(重新评估)。第一次替换该命令后,$chain 变量和第一组引用完成,使其看起来像这样:

http="$(iptables -nvxL http | tail -1 | awk '{print $2}')"

然后将其计算为对命令扩展的变量赋值iptables。由于内部命令使用单引号,我无法使用它们来引用该eval语句,因此使用"\"\""来引用它进行 eval 并根据结果命令中的需要传递双引号和单引号。可能不需要命令扩展周围的双引号,但如果命令扩展为多个单词,我会遇到一些 shell 问题。要转储这些内容,请执行类似的操作:

for chain in http https ssh; do
    eval echo \$$chain
done

第一个美元符号被引用并通过。这会导致循环展开后执行以下三个命令:

echo $http
echo $https
echo $ssh

相关内容