从变量获取值 – 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/dir1
→INPUT1='/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目前这是
-
其次是e
,E
或n
。 但你可能应该考虑任何开始-
如果未来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
除非你自己设置它。
2 : echo hello world
确实有效:它打印hello world
,就像echo 'hello world'
。 这是因为echo
在每个回显参数之间打印一个空格。 然而, echo hello world
并不等同于echo 'hello world'
:前者仍打印hello world
而后者打印hello world
。
3 :有关bash
的printf
内置的更多信息,请参阅help printf
的输出或Bash手册的这一部分 。 printf
也是一个可以从其他shell调用的可执行文件 (并且在该容量中是标准化的 )。