Bash 如果进程产生了xclip的后台分支,则输出重定向将挂起

Bash 如果进程产生了xclip的后台分支,则输出重定向将挂起,bash,pipe,zsh,pipeline,xclip,Bash,Pipe,Zsh,Pipeline,Xclip,我有一个脚本,在里面调用xclip。大概是这样的: #!/bin/bash RESULT="some data" echo $RESULT | xclip echo $RESULT xclip将数据从其stdin放入剪贴板,生成一个后台线程以保留该缓冲区(这就是剪贴板在X11中的工作方式),并将其与tty分离 如果我直接运行脚本,它将按预期工作: $ ./script.sh some data $ 但是,如果我尝试将其输出传递到管道中,它将一直挂起,直到xclip后台进程结束(基本上,直到其

我有一个脚本,在里面调用
xclip
。大概是这样的:

#!/bin/bash
RESULT="some data"
echo $RESULT | xclip
echo $RESULT
xclip
将数据从其stdin放入剪贴板,生成一个后台线程以保留该缓冲区(这就是剪贴板在X11中的工作方式),并将其与tty分离

如果我直接运行脚本,它将按预期工作:

$ ./script.sh
some data
$
但是,如果我尝试将其输出传递到管道中,它将一直挂起,直到
xclip
后台进程结束(基本上,直到其他人将数据放入剪贴板)

我发现
xclip
有一个
-f
标志,似乎可以解决这个问题

从手册页:

在in模式下调用xclip时,输出级别设置为静默(默认设置),过滤器选项将导致xclip打印通过管道传输到标准in的文本 返回标准输出,未经修改

然而,我试图理解为什么它是这样工作的

我创建了另一个行为方式相同的示例:

echo$(睡眠5&)

另外,我被告知在
fish
中,我的示例不带
-f
标志

因此,问题是:
1.这是shell(
bash
zsh
)的错误还是预期的行为?
2.
-f
标志如何影响该行为?从手册页上看,这似乎不是很相关。
3.如何使其他脚本(例如
sleep
)按照
xclip
使用
-f
标志的方式工作?

默认情况下

  • 所有打开的文件都从父进程传递到子进程。及
  • 创建管道时(a->B),进程B将不会在标准输入上获取EOF指示器,直到步骤a中的所有进程关闭其标准输出
  • 结合以上内容,当您执行
    /script.sh |/other_script.sh
    时,后台的
    xclip
    将继承'script.sh'标准输出(它是
    /other_script.sh
    的输入),此时,有两个进程(script.sh,xclip)连接到管道。只有当两者都将关闭STDOUT(或终止)时,
    /other_script.sh
    才会看到管道上的EOF,并且能够退出(假设它正在等待输入上的EOF)

    在这些情况下,解决方案是通过后接地过程关闭标准输出,从而完成管道。通常,如果需要输出,stdout将被重定向到stderr。最有可能的情况是,'-f'关闭标准输出

    Per man
    xclip
    ,可以编写
    echo$RESULT|xclip-f
    (不需要额外的echo)来避免问题,并允许脚本作为管道的一部分运行

    man
    fork
    和man
    pipe
    提供了详细信息。特别是

    男2叉:

    • 子级继承父级打开的文件描述符集的副本。子文件中的每个文件描述符都引用相同的 打开文件描述(请参见打开(2))作为父级中相应的文件描述符。这意味着两个文件描述 TOR共享打开文件状态标志、文件偏移量和信号驱动I/O属性(参见F_SETOWN和 F_SETSIG 在fcntl(2)中
    man 7管道:

    如果引用管道写入端的所有文件描述符都已关闭, 然后尝试从管道读取(2)将看到 文件结尾(读取(2)将返回0)


    所以像
    {echo$RESULT | xclip;}>/dev/null
    这样的东西可以修复它吗?我无法验证这一点。根据手册页,看起来“xclip-f”执行了上述功能,使其可以作为管道的一部分使用。我会扩展答案。谢谢你的详细解释。如果您能从手册页中添加一些描述此行为的链接或引用,将不胜感激。
    $RESULT=“some data”
    ->
    RESULT=“some data”
    $ ./script.sh | ./another_script.sh # it hangs