【问题标题】:How to redirect an output file descriptor of a subshell to an input file descriptor in the parent shell?如何将子shell的输出文件描述符重定向到父shell中的输入文件描述符?
【发布时间】:2021-11-28 19:54:31
【问题描述】:

(在 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 的黑盒程序。

【问题讨论】:

    标签: bash shell file-descriptor


    【解决方案1】:

    注意,BASHISM AHEAD(有些 posix shell 比 bash 快得多,例如 ash 或 dash,它们没有进程替换)。

    您可以做一个句柄舞来将原始标准输出移动到新的描述符,以使标准输出可用于管道(从我的头顶):

    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)
    

    &lt;() 构造是一种bashism。您可以将其替换为管道

    run_in_subshell | while read line; do
        process $line
    done
    

    除了第二个命令subshel​​l中运行,因为管道中的所有命令都这样做。

    【讨论】:

    • 您的解决方案非常鼓舞人心!我认为它几乎可以做到。我刚刚提交了对此解决方案的编辑。通过在子外壳之外交换 &1 和 &3,我们可以避免更改原始子外壳。但是我不确定您的第二个解决方案(使用 | )是否可以工作,因为管道之后的 while 循环也是 BASH 中的子外壳。
    • @user716468:您必须在子shell 中交换 &3 和 &1,因为管道和进程替换都只能使用 &1。是的,稍后循环也在子shell中运行。不幸的是,你可以从非 bash 中得到所有这些。这有点痛苦,但由于父子节点大部分是等价的,通常您可以将脚本的其余部分移到管道中的接收命令中。
    • 当然 bash 解决方案也适用于其他 shell,例如 zsh。但出于性能原因,它在通常用作 /bin/sh 的更简单的 shell 中不起作用。
    • bashism 免责声明非常有帮助!
    • “跳把手舞”——我觉得这就是我打开车门的方式:D 喜欢它
    【解决方案2】:

    最简单的方法当然是直接在parent中捕获输出

    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 中引入了协同进程。

    【讨论】:

    • 在子shell中捕获数据的变量对父shell不可见。
    • 当使用命名管道时,子shell必须与父shell同时运行(使用&),否则它将阻塞在">/tmp/fifo"。在这种情况下需要更多的同步。
    • @user716468 当然,我从来没有打算在子shell 中捕获数据。我澄清了我的回答。如果您已经知道问题的答案,那您为什么还要问?
    • 感谢您的回答和澄清。不,我还没有找到答案。可变方式可能不起作用,因为我希望子外壳使用 STDOUT 打印其他内容,同时将一些数据传递给父级。
    • 这里的进程替换难道不够吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-05
    • 1970-01-01
    • 1970-01-01
    • 2019-10-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多