如何将子shell的输出文件描述符重定向到父shell的输入文件描述符?(涉及IT技术)

38
(在BASH中)我想让一个子shell使用一个非STDOUT非STDERR文件描述符将一些数据传回父shell。我该怎么做?最终我希望将数据保存到父shell的某个变量中。
(
  # The following two lines show the behavior of the subshell.
  # We cannot change them.
  echo "This should go to STDOUT"
  echo "This is the data I want to pass to the parent shell" >&3
)
#...
data_from_subshell=... # Somehow assign the value of &3 of the
                       # subshell to this variable

编辑: 子shell运行一个黑盒程序,向STDOUT和&3写入内容。

2个回答

36

注意,下文使用了Bashism(有一些POSIX shell比如ash或dash比bash更快,但是它们没有进程替换)。

你可以通过一个句柄舞来将原始标准输出移动到一个新描述符中,以便将标准输出用于管道操作(这是我能想到的方法):

exec 3>&1 # open 3 to the same output as 1
run_in_subshell() { # just shortcut for the two cases below
    echo "This goes to STDOUT" >&3
    echo "And this goes to THE OTHER FUNCTION"
}

现在你应该能够编写:
while read line; do
    process $line
done < <(run_in_subshell)

但是<()结构是一种bashism。您可以用管道替换它。

run_in_subshell | while read line; do
    process $line
done

除了第二个命令外,所有管道中的命令都会在子shell中运行,这也包括第二个命令。请注意保留HTML标签。

你的解决方案非常鼓舞人心!我认为它几乎可以做到。我刚刚提交了对这个解决方案的编辑。通过在子shell外交换&1和&3,我们可以避免改变原始子shell。但是我不确定你的第二个解决方案(使用|)是否可行,因为管道后面的while循环也是BASH中的子shell。 - user716468
@user716468:你必须在子shell内交换&3和&1,因为管道和进程替换只能接受&1。是的,在后者中,循环也在一个子shell中运行。这是非bash内容的全部了。有点麻烦,但是由于父进程和子进程大多是等价的,通常可以将脚本的其余部分移动到管道中的接收命令中。 - Jan Hudec
当然,bash解决方案在其他shell中也可以工作,比如_zsh_。但是由于性能原因,在经常用作/bin/sh的简单shell中无法工作。 - Jan Hudec
1
Bashism免责声明非常有用! - Roy Truelove
1
“做一个手柄舞” - 我感觉这就是我打开车门时所做的事情:D 真喜欢它。 - Peter M. Elias

6
当然,最简单的方法是直接在父级中捕获输出。
data_from_subshell=$(echo "This is the data I want to pass to the parent shell")

您可以使用命名管道作为从子进程读取数据的替代方法。

mkfifo /tmp/fifo

现在您可以将子进程重定向到 /tmp/fifo
(
    echo "This should go to STDOUT"
    echo "This is the data I want to pass to the parent shell" >/tmp/fifo
) &

父级可以从那里读取

read data_from_subshell </tmp/fifo

另一种方法是使用coproc来启动一个子进程。这将创建一个带有双向管道的子进程,并将子进程的标准输入和标准输出重定向到管道描述符。要在子进程中同时使用管道和标准输出,您必须首先在父进程中复制标准输出。

exec 4>&1 # duplicate stdout for usage in client

coproc SUBSHELL (
    exec 3>&1 1>&4- # redirect fd 3 to pipe, redirect fd 1 to stdout
    (
    echo "This should go to STDOUT"
    echo "This is the data I want to pass to the parent shell" >&3
    )
)

exec 4>&- # close fd 4 in parent
read data <&${SUBSHELL[0]}
echo "Parent: $data"

协处理器是在Bash 4.0中引入的。


1
捕获子shell中数据的变量对于父shell不可见。 - user716468
当使用命名管道时,子shell必须与父shell并行运行(使用&),否则它将在“>/tmp/fifo”处阻塞。在这种情况下需要更多的同步。 - user716468
这里难道不应该只需要使用进程替换吗? - Jan Hudec
@user716468 需要对文件描述符进行一些调整,但是你可以解决问题,使其适合。请查看修改后的答案。 - Olaf Dietsche
@user4838443 谢谢,我已经将你关于Bash版本的提示添加到我的答案中。 - Olaf Dietsche
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接