在bash中退出管道

在bash中退出管道,bash,shell,pipe,quit,Bash,Shell,Pipe,Quit,对于以下bash语句: tail -Fn0 /tmp/report | while [ 1 ]; do echo "pre"; exit; echo "past"; done 我得到了“pre”,但没有退出到bash提示符,然后如果我在/tmp/report中输入了一些内容,我就可以退出这个脚本并进入bash提示符 我认为这是合理的。“exit”使“while”语句退出,但“tail”仍然有效。如果输入到/tmp/report,则“tail”将输出到管道,然后“tail”将检测到管道关闭,然后

对于以下bash语句:

tail -Fn0 /tmp/report | while [ 1 ]; do echo "pre"; exit; echo "past"; done
我得到了“pre”,但没有退出到bash提示符,然后如果我在/tmp/report中输入了一些内容,我就可以退出这个脚本并进入bash提示符

我认为这是合理的。“exit”使“while”语句退出,但“tail”仍然有效。如果输入到
/tmp/report
,则“tail”将输出到管道,然后“tail”将检测到管道关闭,然后“tail”退出

  • 我说得对吗?如果没有,是否有人提供正确的解释
  • 是否可以在“while”语句中添加任何内容以立即从整个管道语句中退出?我知道我可以将tail的pid保存到一个临时文件中,然后在“while”中读取该文件,然后杀死tail。有没有更简单的方法
  • 让我扩大我的问题。如果在脚本文件中使用此尾部,是否可以同时完成以下项目? A.如果输入Ctrl-C或向主shell进程发送信号,则主shell以及主shell生成的各种子shell和后台进程将退出 B我可以从tail退出,而只能在触发情况下退出,并保持其他子进程继续运行 C最好不要使用临时文件或管道文件
    你说得对。
    while
    循环在子shell中执行,因为它的输入被重定向,
    exit
    只是从该子shell退出

    如果您运行的是
    bash4.x
    ,则可以通过协进程实现所需的功能

    coproc TAIL { tail -Fn0 /tmp/report.txt ;}
    while [ 1 ]
    do
        echo "pre"
        break
        echo "past"
    done <&${TAIL[0]}
    kill $TAIL_PID
    
    coproc TAIL{TAIL-Fn0/tmp/report.txt;}
    而[1]
    做
    回音“pre”
    打破
    呼应“过去”
    完成$pipe&
    TAIL_PID=$!
    而[1]
    做
    回音“pre”
    打破
    呼应“过去”
    完成您可以(不可靠地)杀死进程组:

    tail -Fn0 /tmp/report | while :
    do 
      echo "pre"
      sh -c 'PGID=$( ps -o pgid= $$ | tr -d \  ); kill -TERM -$PGID'
      echo "past"
    done
    
    这可能会将信号发送到比您想要的更多的进程。如果您在交互式终端中运行上述命令,您应该可以,但在脚本中完全可能(确实可能)进程组将包含运行该命令的脚本。为了避免发送信号,最好启用监控并在后台运行管道,以确保为管道形成新的流程组:

    #!/bin/sh
    
    # In Posix shells that support the User Portability Utilities option
    # this includes bash & ksh), executing "set -m" turns on job control. 
    # Background processes run in a separate process group.  If the shell
    # is interactive, a line containing their exit status is printed to
    # stderr upon their completion.
    set -m
    tail -Fn0 /tmp/report | while :
    do 
      echo "pre"
      sh -c 'PGID=$( ps -o pgid= $$ | tr -d \  ); kill -TERM -$PGID'
      echo "past"
    done &
    wait
    

    请注意,我已将
    while[1]
    替换为
    while:
    ,因为
    while[1]
    的风格很差。(其行为与[0]
    时的行为完全相同)。

    感谢您的帮助!现在我的知识在增长。我的bash是3.2.5。也许你的是最好的,而我仍在期待其他一些。你可以使用后台进程向命名管道写入来模拟协进程。添加了使用命名管道的解决方案。当读取器关闭某个管道时,该管道的编写器会出错。如果在循环内执行重定向,则每次通过循环都会打开和关闭它,第一次关闭会杀死写入程序。在第二个版本中,管道的读取端在整个循环中打开一次,只有在循环完成时才关闭。在我测试的情况下,集合-m可能重复,尽管背景与否似乎相同,但在这两种情况下,尾和尾都在同一个新流程组中。您能解释一下关于后台的更多信息吗?如果启用了作业控制,标准要求后台管道作为单独的进程组运行。尽管shell可以将前台组作为不同的进程组运行,但它不是必需的。
    #!/bin/sh
    
    # In Posix shells that support the User Portability Utilities option
    # this includes bash & ksh), executing "set -m" turns on job control. 
    # Background processes run in a separate process group.  If the shell
    # is interactive, a line containing their exit status is printed to
    # stderr upon their completion.
    set -m
    tail -Fn0 /tmp/report | while :
    do 
      echo "pre"
      sh -c 'PGID=$( ps -o pgid= $$ | tr -d \  ); kill -TERM -$PGID'
      echo "past"
    done &
    wait