使用“|”来自 Bash 的 if 构造阻止设置变量

Using '|' from Bash's if construct prevents variable being set

我了解到您可以将 I/O 重定向到 Bash 中的各种结构(例如 "if" 和 "while")。当我尝试这个时,我注意到使用“|”在 if 构造的末尾阻止我在 if 构造中覆盖的任何变量生效;但是如果我改用“>”,那么变量修改就会生效。

#!/bin/bash

VAR_0='Unmodified'

if true
then
    VAR_0='Changed'
fi | cat

echo "VAR_0: $VAR_0"

###########

VAR_1='Unmodified'

if true
then
    VAR_1='Changed'
fi > tmpFile
rm tmpFile

echo "VAR_1: $VAR_1"

运行 Bash 64 位 Linux 版本 4.3.11 产生以下输出:

VAR_0: Unmodified
VAR_1: Changed

请注意,唯一的区别是我如何从 if 结构中重定向标准输出。为什么是“|”防止 VAR_0 被更改?

发生这种情况是因为管道使用子外壳;通过管道连接到 cat,您已经使第一个 if-then 块在子 shell 中执行。因此,VAR_0='Changed' 已更改,但仅在子 shell 中更改。

尝试:

#!/bin/bash

VAR_0='Unmodified'

if true
then
    VAR_0='Changed'
    echo "VAR_0: $VAR_0"
fi | cat

echo "VAR_0: $VAR_0"

重定向到文件 (">") 不会 创建子 shell;因此该变量仍然存在。

TL;DR

查看 Bash FAQ 24。 它回答了您的问题并提供了示例!


您正在创建一个 SubShell,它又连同父 shell 的变量副本一起被丢弃。

以下常见错误为例:

var=0
some-command | while read -r line; do
    printf 'var value inside subshell is: %s\n' "$((++var))"
done
printf 'But in the parent shell it stays: %s\n' "$var"

输出:

var value inside subshell is: 1
But in the parent shell it stays: 0

当不需要这种行为时,解决方案通常是使用进程替换 来保持变量更新。因此,在我们的例子中(或者在 Bash FAQ 24 中通常可以看到)我们执行以下操作:

var=0
while read -r line; do
    printf 'var value inside subshell is: %s\n' "$((++var))"
done < <(some-command)
printf 'And it stays the same: %s\n' "$var"

输出:

var value inside subshell is: 1
And it stays the same: 1