从变量获取值 - bash 脚本

从变量获取值 - 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

答案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

也可以看看:http://www.cyberciti.biz/faq/bash-for-loop-array/

答案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/dir1INPUT1='/tmp/dir with spaces'

    (只有'引号字符才会受到特殊处理,因为它将充当结束引号。)

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

    (即使扩展后的值包含 ,此方法仍有效$。扩展的内容不会再次扩展。
    例如,BAZ=QUUX FOO='BAR $BAZ'; echo "$FOO"打印BAR $BAZ不是 BAR QUUX

  • 如果任何一个可能采取一个值,它将被解释为INPUTnecho选项将要打印的文本替换echoprintf '%s\n'3

    目前,它-后面跟着一个eEn。但您可能应该将以 开头的任何内容-视为危险,以防将来的 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 调用的可执行文件(并以此身份标准化)。

相关内容