【问题标题】:Grep without filteringGrep 不过滤
【发布时间】:2014-12-12 16:16:18
【问题描述】:

如何在不实际过滤或突出显示的情况下进行 grep?

目标是在不影响输出的情况下找出某个文本是否在输出中。我可以 tee 到一个文件,然后离线检查文件,但是,如果输出很大,那是浪费时间,因为它只在处理完成后处理输出:

command | tee file
file=`mktemp`
if grep -q pattern "$file"; then
  echo Pattern found.
fi
rm "$file"

我想我也可以使用 grep 的前 (-B) 和后 (-A) 标志来实现实时处理,但如果没有匹配项,则不会输出任何内容。

# Won't even work - DON'T USE.
if command | grep -A 1000000 -B 1000000 pattern; then
  echo Pattern found.
fi

有没有更好的方法来实现这一点?类似于“假装你正在 grep 并设置退出代码,但不要 grep 任何东西”。

(真的,我要做的是管道stderr,因为我正在寻找某个错误,所以我将使用command | ...而不是command 2> >(... >&2; result=${PIPESTATUS[*]}),它实现了相同的效果,只有它适用于stderr。 )

【问题讨论】:

  • 我不想抑制任何命令的输出。
  • 您可以实现您想要的,但问题是一旦grep 找到匹配项,command 也会被终止,从而无法保留输出(因为命令得到停在中间)。您能解释一下您要实现的目标是什么吗?
  • 目标是找出某个错误是否由程序输出。我希望在错误发生时也将错误打印到终端,因为错误可能取决于也在标准输入上输出的上下文,并且将所有错误缓存到文件并仅在最后回显它们会破坏这种关系。

标签: bash shell grep


【解决方案1】:

如果您只想在找到模式时设置退出代码,那么这应该可以解决问题:

awk -v rc=1 '/pattern/ { rc=0 } 1; END {exit rc}' 

-v rc=1 在 Awk 程序中创建一个名为 rc(“返回码”的缩写)的变量并将其初始化为值 1。/pattern/ { rc=0 } 节会导致该变量在任何时候设置为 0遇到匹配正则表达式pattern1; 是一个始终为真的条件,没有附加任何操作,这意味着将对每一行执行默认操作;该默认操作是将行打印出来,因此此过滤器将其输入复制到其输出不变。最后,END {exit rc} 在没有更多输入需要处理时运行,并确保awkrc 变量的值作为其进程退出状态终止:如果找到匹配则为 0,否则为 1。

shell 将退出代码 0 解释为真,非零解释为假,因此该命令适合用作 shell ifwhile 语句的条件,可能位于管道的末尾。

【讨论】:

    【解决方案2】:

    要允许输出搜索结果,您可以使用 awk:

    command | awk '/pattern/{print "Pattern found"} 1'
    

    pattern 在任何行中匹配时,这将打印“找到模式”。 (稍后会打印行)

    如果您希望 Line 在此之前打印,请使用:

    command | awk '{print} /pattern/{print "Pattern found"}'
    

    编辑:要在比赛中执行任何命令:

    command | awk '/pattern/{system("some_command")} 1'
    

    编辑 2:要注意关键字中的特殊字符,请使用以下命令:

    command | awk -v search="abc*foo?bar" 'index($0, search) {system("some_command"); exit} 1'
    

    【讨论】:

    • 我想知道输出是否包含某个字符串,而不以任何方式修改输出。
    • 是的,我知道,这就是 awk 的作用,除了添加 "Pattern found" 行之外,它根本不会改变输出
    • 由于我不想添加任何行,而是想在找到行时执行代码,我将如何使用 awk 实现呢?我想最坏的情况是我可以让它将信号写入文件? (丑)
    • awk print "Pattern found" 中的那一行可以使用system 函数更改为执行任何代码。
    • 1+,善用AWK。你能解释一下awk '/pattern/{system("some_command")} 1' 中的1 是如何打印所有输入行的吗?
    【解决方案3】:

    我认为您想打印输出的每一行,但同时跟踪是否找到了特定模式。简单地将输出传递给sedgrep 会影响输出。你需要做这样的事情:

    pattern=0
    command | while read line
    do
        echo "$line"
        if grep -q "$pattern" <<< "$lines"
        then
            ((pattern+=1))
        fi
    done
    if [[ $pattern -gt 0 ]]
    then
        echo "Pattern was found $pattern times in the output"
    else
        echo "Didn't find the pattern at all"
    fi
    

    附录

    如果原始命令同时具有 stdout 和 stderr 输出,它们以特定顺序出现,并且两者可能交错,那么您的解决方案会确保输出像往常一样交错吗?

    好的,我想我明白你在说什么了。您希望 STDERR 和 STDOUT 都被用于此模式。

    STDERR 和 STDOUT 是两个不同的东西。它们都出现在终端窗口上,因为这是您放置它们的地方。 管道 (|) 只接受 STDOUT。 STDERR 被单独留下。在上面,仅使用 STDOUT 的输出。如果你想要 STDOUT 和 STDERR,你必须重定向 STDERR 到 STDOUT:

    pattern=0
    command 2>&1 | while read line
    do
        echo "$line"
        if grep -q "$pattern" <<< "$lines"
        then
            ((pattern+=1))
        fi
    done
    if [[ $pattern -gt 0 ]]
    then
        echo "Pattern was found $pattern times in the output"
    else
        echo "Didn't find the pattern at all"
    fi
    

    注意2&gt;&amp;1。这表示采用 STDERR(即文件描述符 2)并将其重定向到 STDOUT(文件描述符 1)。现在,两者都将通过管道传输到 while read 循环中。

    grep -q 将阻止 grep 将其输出打印到 STDOUT。它将打印到 STDERR,但在这种情况下这不应该是一个问题。只有当 Grep 无法打开请求的文件或模式丢失时,Grep 才会打印出 STDERR。

    【讨论】:

    • 在运行命令时,stdout 和 stderr 是否会像往常一样交错打印?
    • @MihaiDanila Interleave 在哪里?你是在说grep吗? grep -q 将停止打印 STDOUT。要使 STDERR 静默,您需要将 2&gt; /dev/null 附加到命令中。但是,grep 通常仅在未找到您指定的文件时才打印到 STDERR(此处不是这种情况)。
    • @MihaiDanila Interleave 在哪里?你是在说grep吗? grep -q 将阻止 STDOUT 打印。要使 STDERR 静默,您需要将 2&gt; /dev/null 附加到命令中。但是,grep 通常仅在未找到您指定的文件时才打印到 STDERR(此处不是这种情况)。
    • 如果原始命令同时具有 stdout 和 stderr 输出,它们以特定顺序出现,并且两者可能交错,那么您的解决方案会确保输出像往常一样交错吗?
    • 请参阅我的回答中的附录。
    【解决方案4】:

    你可以这样做:

    echo "'search string' appeared $(command |& tee /dev/stderr | grep 'search string' | wc -l) times"
    

    这将打印command 的整个输出,后跟以下行:

    'search string' appeared xxx times
    

    诀窍在于,tee 命令不用于将副本推送到文件中,而是将stdout 中的所有内容复制到stderrstderr 流立即显示在屏幕上,因为它没有连接到管道,而 stdout 上的副本被 grep/wc 组合吞噬。

    由于错误消息通常发送到stderr,并且您说要使用grep 查找错误消息,因此|&amp; 运算符用于第一个管道将commandstderr 组合到其@ 987654335@,并将两者都推送到tee 命令中。

    【讨论】:

      【解决方案5】:

      试试这个脚本。它不会修改your-command 的任何输出,当找到模式时 sed 退出时为 0,否则为 1。根据我对您的问题和评论的理解,我认为这是您想要的。:

      if your-command | sed -nr -e '/pattern/h;p' -e '${x;/^.+$/ q0;/^.+$/ !q1}'; then
        echo Pattern found.
      fi
      

      下面是一些测试用例:

      ubuntu-user:~$ if echo patt | sed -nr -e '/pattern/h;p' -e '${x;/^.+$/ q0;/^.+$/ !q1}'; then       echo Pattern found.;     fi
      patt
      ubuntu-user:~$ if echo pattern | sed -nr -e '/pattern/h;p' -e '${x;/^.+$/ q0;/^.+$/ !q1}'; then       echo Pattern found.;     fi
      pattern
      Pattern found.
      

      请注意,当没有来自 your-command 的输出时,之前的脚本无法运行,因为这样 sed 将不会运行 sed 表达式并始终以 0 退出。

      【讨论】:

        猜你喜欢
        • 2021-09-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-03
        • 2013-04-28
        • 1970-01-01
        • 2012-04-25
        • 2020-01-14
        相关资源
        最近更新 更多