我刚刚在bash
发现了一个我不明白的奇怪行为。表达式
${variable:=default}
如果variable
尚未设置,则将default
设置为值#!/bin/bash
file ${foo:=$1}
echo "foo >$foo<"
file ${bar:=$1} | cat
echo "bar >$bar<"
。请考虑以下示例:
$ ./test myfile.txt
myfile.txt: ASCII text
foo >myfile.txt<
myfile.txt: ASCII text
bar ><
输出结果为:
foo
您会注意到变量$1
的值为bar
,但变量file
不是,即使其默认结果显示在cat
}命令。
如果您从第4行将无害管道移至foo
并重新运行,则bar
和$1
都会设置为bash
<的值/ p>
我在这里错过了什么,或者这可能是{{1}}错误?
(GNU bash,版本4.3.30)
答案 0 :(得分:5)
在第二种情况下,file
是一个管道成员,并在其自己的shell中作为每个管道成员运行。当file
及其子shell结束时,$b
及其$1
的新值不再存在。
解决方法:
#!/bin/bash
file ${foo:=$1}
echo "foo >$foo<"
: "${bar:=$1}" # Parameter Expansion before subshell
file $bar | cat
echo "bar >$bar<"
答案 1 :(得分:2)
这不是一个错误。参数扩展在评估命令时发生,而不是解析,但在新进程启动之前,不会评估作为管道一部分的命令。除了可能破坏某些现有代码之外,更改此内容还需要在进行评估之前进行额外的扩展。
假设的bash
会话:
> foo=5
> bar='$foo'
> echo "$bar"
$foo
# $bar expands to '$foo' before the subshell is created, but then `$foo` expands to 5
# during the "normal" round of parameter expansion.
> echo "$bar" | cat
5
为避免这种情况,bash
需要某种方式来标记新的第一轮预评估参数扩展所产生的文本片段,以便它们不会经历一秒钟
一轮评估。这种类型的簿记会很快导致无法维护的代码,因为发现更多的角落案件被处理。更简单的是接受参数扩展将推迟到子shell开始之后。
另一种方法是允许每个组件在当前shell中运行,这是POSIX标准允许的,但也不是必需的。很久以前bash
做出选择来执行子shell中的每个组件,并且反转会破坏过多依赖于当前行为的现有代码。 (bash
4.2确实引入了lastpipe
选项,如果明确启用,则允许管道的 last 组件在当前shell中执行。)