在bash中退出管道

时间:2013-12-13 02:53:23

标签: 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'将检测到管道已关闭,然后'tail'退出。

  1. 我是对的吗?如果没有,有人会提供正确的解释吗?
  2. 是否可以在'while'语句中添加任何内容以立即退出整个管道语句?我知道我可以把尾巴的pid保存到一个临时文件中,然后在'while'中读取这个文件,然后杀掉尾巴。有更简单的方法吗?
  3. 让我扩大我的问题。如果在脚本文件中使用此尾部|是否可以同时执行以下项目? 一个。如果输入Ctrl-C或发出主shell进程的信号,主shell生成的主shell和各种子shell和后台进程将退出 湾我只能在触发器情况下从尾部退出,并保留其他子进程继续运行 C。最好不要使用临时文件或管道文件。

2 个答案:

答案 0 :(得分:3)

你是对的。 while循环正在子shell中执行,因为它的输入被重定向,而exit只是退出该子shell。

如果您正在运行bash 4.x,您可以通过协处理实现您想要的效果。

coproc TAIL { tail -Fn0 /tmp/report.txt ;}
while [ 1 ]
do
    echo "pre"
    break
    echo "past"
done <&${TAIL[0]}
kill $TAIL_PID

http://www.gnu.org/software/bash/manual/html_node/Coprocesses.html

对于旧版本,您可以使用后台进程写入命名管道:

pipe=/tmp/tail.$$
mkfifo $pipe
tail -Fn0 /tmp/report.txt >$pipe &
TAIL_PID=$!
while [ 1 ]
do
    echo "pre"
    break
    echo "past"
done <$pipe
kill $TAIL_PID
rm $pipe

答案 1 :(得分:1)

你可以(不可靠)逃脱杀死进程组:

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 ]风格较差。 (它的行为与while [ 0 ]完全相同)。