Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/batch-file/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Bash 当在标准错误中看到特定字符串时,如何终止进程?_Bash_Grep_Stderr - Fatal编程技术网

Bash 当在标准错误中看到特定字符串时,如何终止进程?

Bash 当在标准错误中看到特定字符串时,如何终止进程?,bash,grep,stderr,Bash,Grep,Stderr,我需要启动一个流程,比如说foo。我希望stdout/stderr正常,但是grepstringbar的stderr。一旦在中找到条 stderrfoo应该被杀死 这可能吗?我最初写了一种方法来实现这一点,它涉及到流的旋转,但不是很好。有些评论与该版本有关。如果你好奇的话,检查一下历史记录 以下是一种方法: (PIDFILE=$(mktemp /tmp/foo.XXXXXX) && trap "rm $PIDFILE" 0 \ && { foo \

我需要启动一个流程,比如说
foo
。我希望stdout/stderr正常,但是
grep
string
bar
的stderr。一旦在中找到
stderr
foo
应该被杀死

这可能吗?

我最初写了一种方法来实现这一点,它涉及到流的旋转,但不是很好。有些评论与该版本有关。如果你好奇的话,检查一下历史记录

以下是一种方法:

(PIDFILE=$(mktemp /tmp/foo.XXXXXX) && trap "rm $PIDFILE" 0 \
   && { foo \
           2> >(tee >(grep -q bar && kill $(cat $PIDFILE)) >&2) \
        & PID=$! && echo $PID >$PIDFILE ; wait $PID || true; })
好的老式噩梦燃料。这里发生了什么事

  • 最外面的括号把整个东西放在一个次壳里;这限制了变量的范围,以便于卫生
  • 我们使用同时适用于GNU和BSD的语法创建一个临时文件
    mktemp
    ,并将其称为
    PIDFILE
  • 我们设置了一个catch-all-exit陷阱(当最外层的子shell退出时运行),以删除名为
    PIDFILE
    的文件,同样用于hygeine
  • 我们运行
    foo
    ;这是在复合语句中完成的,以便
    &
    绑定到
    foo
    ,而不是绑定到前面的整个管道
  • 我们将
    foo
    的标准错误重定向到一个进程替换中,它等待
    bar
    出现,然后杀死
    foo
    (后面会有更多)
  • 我们将
    foo
    的PID捕获到一个变量中,将其写入名为
    PIDFILE
    的文件中,然后等待它,这样整个命令在退出之前等待
    foo
    退出;发生这种情况时,
    | | true
    将丢弃
    foo
    的错误退出状态
  • 流程替换中的代码如下所示:

  • 首先,
    tee
    输入(
    foo
    的标准错误),将
    tee
    的标准输出重定向到标准错误,以便
    foo
    的标准错误确实出现在标准错误上
  • 将输入的副本发送到另一个进程替换()的文件
  • 在更深层次的进程替换中,首先对输入运行
    grep-q
    ,它查找指定的模式,并在找到它后立即退出(或当它到达流的末尾),而不打印任何内容,然后(如果它找到字符串并成功退出)shell继续
  • kill
    其PID被捕获到名为
    PIDFILE
    的文件中的进程,即
    foo

  • 作为另一个答案的替代方案,一种方法是使用bash的
    coproc
    工具:

    {coproc FOO { foo; } 2>&1 1>&3; } 3>&1
    CHILD=$!
    while read line <&${FOO[0]}; do
        if echo "$line" | grep -q bar; then
            kill $CHILD
        else
            echo "$line"
        fi
    done
    
    {coproc FOO{FOO;}2>&11>&3;}3>&1
    CHILD=$!
    虽然读取行非常好,但只有在
    foo
    自行终止或通过Ctrl-C终止时,
    kill$(cat$PIDFILE)
    才会出现在我的系统上。以下解决方案对我有效

    读取g时
    做
    如果[$g=~bar]]
    然后
    杀死美元!
    fi
    完成<>(tee/dev/tty)
    )
    
    我实际上设法找到了一种方法来实现这一点,而不需要PID文件或协同例程,而且这种方法应该适用于所有POSIX兼容的shell(我已经尝试了
    bash
    dash
    )。至少在支持
    /dev/fd/
    的系统上是这样,但这应该是它们的全部

    不过这有点复杂,所以我不确定你是否喜欢

    (                               # A
        (                           # B
            ( /tmp/foo 2>&1 1>&3 & echo $! >&4 ) |              # C
            ( tee /dev/fd/2 | ( grep -q bar && echo fin >&4 ) ) # D and E
        ) 4>&1 | (                  # F
            read CHILD
            read STATUS
            if [ "$STATUS" = fin ]; then
                kill $CHILD
            fi
        )
    ) 3>&1
    
    为了解释本文使用的众多子shell:

    A
    的主体在正常标准输出复制到fd 3的情况下运行。它运行子shell
    B
    F
    ,stdout为
    B
    ,stdin为
    F

    B
    的主体与fd 4上重复的
    A
    中的管道一起运行

    C
    运行实际的foo命令,其stderr连接到从
    C
    D
    的管道,其stdout从fd3复制;也就是说,恢复到全局标准输出。然后将
    foo
    的PID写入fd 4;也就是说,到subshell
    F
    在其标准DIN上的管道

    D
    运行
    tee
    命令,从管道接收stderr上打印的
    foo
    内容。它将该输出复制到
    /dev/fd/2
    (以便在全局stderr上显示)和连接到subshell
    E
    的管道

    E
    greps表示条形,然后在找到时,在fd 4上写入
    fin
    ,即写入其标准DIN上的管道。注意
    和&
    ,确保如果grep遇到EOF而没有找到
    ,则不会写入
    fin

    F
    ,然后从
    C
    读取PID,从
    E
    读取
    fin
    终止符。如果
    fin
    终止符正确输出,则会杀死
    foo

    编辑:修复丢失的
    T形三通
    ,将foo的标准复制到真实标准。

    使用Expect监视标准错误 Expect用于根据流程的输出采取操作。最简单的解决方案是让Expect启动流程,然后在看到预期输出时退出。例如:

    expect -c 'set msg {Saw "foo" on stderr. Exiting process.}
               spawn /bin/bash -c "echo foo >&2; sleep 10"
               expect "foo" { puts $msg; exit }'
    

    如果生成的进程正常结束(例如,在看到“foo”之前),那么Expect脚本也将退出。

    我的想法是,有一种更好的方法可以做到这一点,即将
    grep&&kill
    放在从foo获得
    2>
    的进程替换中;这样一来,
    foo
    的流就不会被屏蔽,而且通常会更简单。@Dolda2000:With
    -q
    grep
    在找到第一行匹配后就会退出。不过,我真的认为这不重要。如果我没有弄错的话,这应该适用于任何与POSIX兼容的东西。可能是因为当您的
    foo
    程序连接到管道时缓冲了它的stderr而没有刷新它吗?是的,这是意料之中的<代码>读取状态
    F
    等待的位置