从变量获取值 – bash脚本

我正在尝试编写一个bash脚本来打印3个变量的值。 不断收到错误。 我究竟做错了什么?

INPUT1=/tmp/dir1 INPUT2=/tmp/dir2 INPUT3=/tmp/dir3 for i in 1 2 3 do echo $(INPUT$i) done 

当我运行这个脚本时,输出是:

 syntax error: operand expected (error token is "/tmp/dir1 

Bash不直接支持这种语法。 您可以使用’eval’,但是对于’bash’的现代版本,imho是通过数组最干净的方式

 input=( "/tmp/dir1" "/tmp/dir2" "/tmp/dir3" ) for i in {0,1,2}; do echo "${input[i]}"; done 

(注意bash数组是零索引的); 或列出您可以使用的所有元素

 for i in "${input[@]}"; do echo "$i"; done 

我同意使用其他答案中显示的数组是解决OP真正任务的更好方法。
但是这些答案并没有直接回答所述的问题,所以我会这样做:

以下是使用eval来访问变量的方法,紧跟OP给出的代码方式:

 INPUT1=/tmp/dir1 INPUT2=/tmp/dir2 INPUT3=/tmp/dir3 for i in {1..3} ; do eval "echo \$INPUT$i" done 

输出:

 /tmp/dir1 /tmp/dir2 /tmp/dir3 

最好的方法是将INPUT变量定义为数组:

 #!/bin/bash INPUT[1]=/tmp/dir1 INPUT[2]=/tmp/dir2 INPUT[3]=/tmp/dir3 for i in "${INPUT[@]}" do echo $i #or $INPUT[$i] done 

另见: http : //www.cyberciti.biz/faq/bash-for-loop-array/

bash实际上支持的东西非常接近你第一次尝试的东西。

 INPUT1=/tmp/dir1 INPUT2=/tmp/dir2 INPUT3=/tmp/dir3 for s in INPUT{1..3}; do echo ${!s} done 

这是因为:

  • INPUT{1..3}扩展为INPUT1 INPUT2 INPUT3 。 (它相当于INPUT{1,2,3} 。)

  • 其中$s$s的扩展, ${!s}${!s}扩展的扩展

    例如,在循环的第一次迭代中,如果出现$s ,它将扩展为INPUT1 。 如果出现$INPUT1 ,它将扩展为/tmp/dir1 。 因此${!s}扩展为/tmp/dir1

    这种参数扩展称为间接扩展 。 在这种情况下,很多人喜欢它,内置! 语法比eval更紧凑,更优雅。

    有关更多信息,请参阅是否可以在bash中从其他变量构建变量名称? 在Stack Overflow上 。

一些警告(也适用于已发布的其他一些答案):

  • 如果INPUT n任何值可能包含空格 1 (如空格2 , 制表符或换行符 )或具有可扩展的特殊字符的构造 (例如$varname ),则应使用引号 。

    如果要阻止所有扩展,则分配的单引号最佳:
    INPUT1=/tmp/dir1INPUT1='/tmp/dir with spaces'

    (只有'引用字符被特别处理,因为它将作为结束引号。)

    但是使用双引号仍然允许变量扩展:
    echo ${!s}echo "${!s}"

    (即使扩展后的值包含$这也有效。扩展的内容不会再次展开。
    例如, BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO" BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO"打印BAR $BAZ而非 BAR QUUX 。)

  • 如果任何INPUT n可能采用echo将其解释为选项而不是要打印的文本的值,则将echo替换为printf '%s\n'3

    目前这是-其次是eEn 。 但你可能应该考虑任何开始-如果未来bash版本添加新选项的危险。


虽然在语法方面,如上所述的间接扩展几乎与使用数组一样简单,但您仍然可能希望使用数组,因为:

  • 它可能更好地反映您尝试解决的问题的潜在含义。
  • 在解决此问题时,您可能会使用数组的其他function。

也许您希望/tmp/dir1/tmp/dir2/tmp/dir3作为可以被任何东西替换的不透明示例。

但是如果你真的想要制作这些特定值的数组,我建议:

 input=(/tmp/dir{1..3}) 

类似地,如果您的目标只是遍历/tmp/dir1/tmp/dir2/tmp/dir3并对每个执行某些操作,那么您不需要将它们存储在任何类型的变量中

 for s in /tmp/dir{1..3}; do echo $s done 

如果s的值将包含空格或特殊字符(参见上文),请引用它们,但通过单独引用文本的左侧和右侧使{...}范围不加引号 – 如果不引用,则不会扩展{...}范围引用,即使是双引号。

 for s in '/tmp/dir '{1..3}' with spaces'; do echo "$s" done 

打印:

 /tmp/dir 1 with spaces /tmp/dir 2 with spaces /tmp/dir 3 with spaces 

如果您的目标只是打印/tmp/dir1/tmp/dir2/tmp/dir3 ,每个都在它自己的行上,那么这个单个命令3就足够了:

 printf '%s\n' /tmp/dir{1..3} 

1 :shell 将文本拆分为空格上的单词 ,或者,如果定义了IFS变量,则将其分配给它指定的字段分隔符。 但是你通常不必担心IFS除非你自己设置它。

2echo hello world确实有效:它打印hello world ,就像echo 'hello world' 这是因为echo在每个回显参数之间打印一个空格。 然而, echo hello world并不等同于echo 'hello world' :前者仍打印hello world而后者打印hello world

3 :有关bashprintf内置的更多信息,请参阅help printf的输出或Bash手册的这一部分 。 printf也是一个可以从其他shell调用的可执行文件 (并且在该容量中是标准化的 )。