我正在尝试编写一个 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
答案1
Bash 不直接支持这种语法。您可以使用“eval”,但我认为,对于现代版本的“bash”,最简洁的方式是通过数组
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
答案2
我同意使用其他答案中所示的数组是解决 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
答案3
最好的方法是将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
答案4
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
。有关详细信息,请参阅For more information, see在 bash 中是否可以从其他变量构建变量名?在堆栈溢出。
一些注意事项(也适用于已发布的其他一些答案):
如果任何值可能包含
INPUTn
空格1(如空格2,标签, 或者换行符) 或者带有特殊字符的结构将被扩展(例如$varname
),引用应该使用。如果要防止所有扩展,最好使用单引号进行赋值:
INPUT1=/tmp/dir1
→INPUT1='/tmp/dir with spaces'
(只有
'
引号字符才会受到特殊处理,因为它将充当结束引号。)但使用双引号仍然允许变量扩展:
echo ${!s}
→echo "${!s}"
(即使扩展后的值包含 ,此方法仍有效
$
。扩展的内容不会再次扩展。
例如,BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO"
打印BAR $BAZ
,不是BAR QUUX
。如果任何一个可能采取一个值,它将被解释为
INPUTn
echo
选项将要打印的文本替换echo
为printf '%s\n'
。3目前,它
-
后面跟着一个e
、E
或n
。但您可能应该将以 开头的任何内容-
视为危险,以防将来的 bash 版本添加新选项。
虽然从语法上讲,上面提到的间接扩展几乎和数组一样简单,你仍然可能希望使用数组因为:
- 它可能更好地反映您试图解决的问题的根本含义。
- 您可能需要使用数组的其他功能来解决这个问题。
或许您打算将/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: 贝壳将文本拆分成单词在空格上,或者如果IFS
定义了变量,则在它指定的字段分隔符上。但通常你不必担心,IFS
除非你自己设置了它。
2:echo hello world
实际上确实有效:它打印hello world
,就像 一样echo 'hello world'
。这是因为echo
在每个回显之间打印一个空格争论。但是,echo hello world
并不等同于echo 'hello world'
:前者仍然打印,hello world
而后者打印hello world
。
3bash
:有关内置功能的更多信息printf
,请参阅输出help printf
或者Bash 手册的这一部分。printf
也是可以从其他 shell 调用的可执行文件(并以此身份标准化)。