如果后面跟管道(bash bug?),Bash变量默认不起作用

时间:2014-10-23 11:12:22

标签: linux bash

我刚刚在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)

2 个答案:

答案 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中执行。)