了解重定向函数调用输出使变量突变不可见的效果

了解重定向函数调用输出使变量突变不可见的效果

我在 mac 中的 zsh 5.9 中观察到一个奇怪的行为。 (以下是重现错误的简化,它并不试图有意义)。如果我在终端中执行

$> function assigner() { OPTIONS=mutated } 
$> function main() { typeset OPTIONS; assigner; echo $OPTIONS }  
$> main

这输出:

mutated

但是如果我巧妙地更改 main 函数以重定向内部函数调用的输出......

$> function main() { typeset OPTIONS; assigner | cat; echo $OPTIONS }

然后结果就是空白

我的 OPTIONS var 不再填充。这里发生了什么?

答案1

管道是一种进程间通信机制。

A | B管道中,A并与通过管道连接到标准输入的B标准输出并行运行。AB

因此它们必须在单独的进程中运行。在大多数 shell 中,它们都在子进程中运行;在 zsh 中,就像 ksh 的原始实现一样,仅A在子进程中运行(尽管如果B是外部命令,当然它仍然会在子进程中执行)。

因此assigner | cat,由于assigner在子 shell 进程中运行,因此它仅修改该子 shell 的变量,而不是父 shell 进程的变量。当管道终止时,对变量的更改将丢失。

在这里,如果您想assigner在父进程中运行,但仍将其输出通过管道传输到cat(在单独的进程中运行),您可以将assigner的输出重定向到正在运行的进程替换cat

assigner > >(cat)

cat或者作为协进程运行:

协进程将其 stdin 和 stdout 重定向到管道,因此主进程既可以向它们提供数据并从它们获取日期,但您可以通过使用额外的文件描述符保存和恢复原始 stdout 来取消 stdout 部分。

assigner() OPTIONS=mutated

{ coproc cat >&3 3>&-; } 3>&1 # start cat as coproc with stdout restored
cat_pid=$!                    # record its pid
assigner >&p                  # run assigner with stdout redirected to coproc
coproc :                      # dummy coproc to close the pipes to the
                              # previous one.
wait $cat_pid                 # make sure cat has returned before running
                              # the next commands.
echo $OPTIONS                 

(看如何在各种 shell 中使用 coproc 命令?有关如何使用协同进程的更多详细信息)。

或者,您可以利用assigner在运行它的同一子 shell 中设置的变量,再次使用单独的 fd 使原始 stdout 可用:

assigner() OPTIONS=mutated

{
  {
    assigner
    echo $OPTIONS >&3
  } | cat
} 3>&1

相关内容