【问题标题】:Detect if piped command contains string in bash检测管道命令是否在bash中包含字符串
【发布时间】:2021-11-20 22:54:58
【问题描述】:

我有一个 bash 脚本,其中一行如下所示:

Command1 | Command2 | Command3

Command1 产生输出,其他命令(Command2 和 Command3)过滤输出。

过滤是逐行实时进行的(使用 sed)。我不能等待 Command1 完成过滤。

我想知道 Command1 的输出是否包含字符串(例如“foo bar\n\n”)。 我想在 Command1 完成时知道这一点。如您所见,我要查找的字符串是多行。

这可能吗?

【问题讨论】:

  • 所以你想要command1 | tee tempfile | command2 | command3 ; if tempfile contains your string; then something; fi
  • 请注意,command1 完成和 command3 完成之间的时间不太可能很长,因为它们都是并行运行的。

标签: bash


【解决方案1】:

从进程替换扫描

如果您创建一个 shell 函数 testcmd 来检查您的字符串并在看到它时采取行动(请注意,此行动可能类似于运行程序或创建文件;您设置的任何变量都不会对启动管道的父 shell 可见),这看起来像:

findit() {
  awk '
    BEGIN { rc=1 }  # starting: unless we find the pattern, exit w/ an error
    $0 ~ /foo bar$/ && rc == 1 { rc=0; next } # found the first line
    $0 == ""        && rc == 0 { exit }       # success; found our pattern
    { rc=1 }        # reset: saw a line that did not trigger a next or exit above
    END { exit(rc) }                          # honor rc as exit status
  '
}
testcmd() { findit && { echo "Found the pattern" >&2; touch found; }; }
Command1 | tee >(testcmd) | Command2 | Command3

此代码将在Command1 的输出中看到foo bar\n\n(假设\ns 旨在表示文字换行符)时创建found 文件,而无需等待Command2 或@987654329 @(或者甚至等待 Command1 完成,如果在发出此字符串后它还有更多输出)。

>(...) 语法是process substitution,它提供了一个文件名,可用于写入... 中复合命令的标准输入。

这不会中断Command2Command3 的操作,因为tee 会忽略一个提前停止读取的输出,并继续将内容传递给它的其他输出,只要有更多输入可用并且至少一个输出正在接受写。


测试上述内容

为了测试上述逻辑,我们可以定义如下函数:

Command1() {
  printf '%s\n' 'first line' 'prefix foo bar' '' 'last line'
}

Command2() {
  echo "command2 reading" >&2
  in=$(cat)

  sleep 1

  echo "command2 writing" >&2
  printf '%s\n' "$in"
}

Command3() { echo "command3 read $(wc -l) lines"; }

...Command1 | tee >(testcmd) | Command2 | Command3 的组合输出是:

command2 reading
Found the pattern
command2 writing
command3 read 4 lines

(您可能在command2 reading 之前有Found the pattern - 它们之间的顺序是未定义的;但重点是Found the pattern 发生在command2 writing 之前,并且也发生在command3 完成之前)。

【讨论】:

  • 我已经删除了我的答案,因为你的答案更彻底。不过echo >&2 可能不是一个好主意,而且在同一个地方多次使用脚本时,只有touch 一个文件容易出错。
  • echo >&2 的目的是将内容排除在管道之外。这完全适合与用户交流——事实上,POSIX 将 stderr 指定为用于“诊断”目的的正确事物,这是一个非常广泛的定义。
  • @Fravadona, ...例如,将shell提示写入stderr;理论上的基本原理是关于程序何时等待输入的信息本质上是诊断性的;实际的理由是它的内容应该被人类而不是另一个程序看到,并且提示应该始终是无缓冲的(因为标准输入是默认的,但标准输出只是有条件的)。
  • @Fravadona, ...至于touch,这是一个示例,OP 打算用他们希望程序在检测到手头的情况时采取的任何操作来替换。也许他们启动了一个 GUI 框架来弹出一个消息框;也许他们向另一个程序发送信号;这是他们的事,而不是我们的事来决定。
  • @Fravadona,(...正如您可能从上面收集到的那样,我讨厌和鄙视的一件事是软件假定如果任何内容已写入 stderr,则意味着错误已发生了;这样的假设完全不符合 POSIX 准则——stderr 适用于所有信息日志,而不仅仅是错误;否则,信息日志将通过管道,因此它们将被 grep-esque 过滤掉内容,会改变 wc 等的结果。
猜你喜欢
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
相关资源
最近更新 更多