【问题标题】:Bash: One-liner to exit with the opposite status of a grep command?Bash:单线以与 grep 命令相反的状态退出?
【发布时间】:2013-02-28 08:19:05
【问题描述】:

如何减少以下 bash 脚本?

grep -P "STATUS: (?!Perfect)" recess.txt && exit 1
exit 0

看来我应该可以用一个命令来完成,但我这里总共有 3 个。

我的程序应该:

  • 阅读recess.txt
  • 退出 1(或非零),如果它包含“STATUS:”的行而不是“完美”
  • 如果不存在这样的行,则退出 0(即所有“STATUS:”行都是“Perfect”)

答案奖授予最紧凑的剧本。谢谢!

示例文件

该文件的程序应该有退出状态0

FILE: styles.css 
STATUS: Perfect!

FILE: contour-styles.css
STATUS: Perfect!

此文件的程序应具有退出状态 1(或非零):

FILE: styles.css 
STATUS: Perfect!

FILE: contour-styles.css
STATUS: Busted 
FAILURES: 1 failure

Id's should not be styled
       1. #asdf

【问题讨论】:

  • 这应该在 PPCG 上进行。

标签: regex bash grep exit exit-code


【解决方案1】:

只是negate返回值。

! grep -P "STATUS: (?!Perfect)" recess.txt

【讨论】:

  • 这就是我要找的。谢谢
  • 整洁。短的。优雅的。管道到 grep 时有没有办法使这项工作?
  • @wedi cat recess.txt | ( ! grep -P "STATUS: (?!Perfect)" ) 可以工作(它在 subshell 中运行 grep)。 cincodenada 答案中的技巧也可以。
  • 如果 grep 返回 2 怎么办 - 发生错误。我猜失败会是更好的结果
  • @feech 取决于用例。发生错误时,grep 会写入标准错误,这可能是检测它的更好方法。否则,您可以像 cincodenada 的回答一样直接检查状态。
【解决方案2】:

我遇到了这个问题,需要 Puppet 的 onlyif 声明。因此,Tgr's bash solution 不起作用,我不想像Christopher Neylan's answer 那样扩展复杂性。

我最终使用了受 Henri Schomäcker's answer 启发的版本,但明显简化了:

grep -P "STATUS: (?!Perfect)" recess.txt; test $? -eq 1

这非常简单地反转了退出代码,只有在找不到文本时才返回成功:

  • 如果 grep 返回 0(找到匹配项),test 0 -eq 1 将返回 1。
  • 如果 grep 返回 1(未找到匹配项),test 1 -eq 1 将返回 0。
  • 如果 grep 返回 2(错误),test 2 -eq 1 将返回 1。

这正是我想要的:如果没有找到匹配项,则返回 0,否则返回 1。

【讨论】:

  • 也适用于grep -v(反转)。
【解决方案3】:

要使其与set -e 一起使用,请将其包围在带有() 的子shell 中:

$ cat test.sh 
#!/bin/bash

set -ex
(! ls /tmp/dne)
echo Success
$ ./test.sh 
+ ls /tmp/dne
ls: cannot access /tmp/dne: No such file or directory
+ echo Success
Success
$ mkdir /tmp/dne
$ ./test.sh 
+ ls /tmp/dne
$ 

【讨论】:

    【解决方案4】:

    如果有人来这里寻找 bash 返回码操作:

    (grep <search> <files> || exit 0 && exit 123;)
    

    这将在 grep 没有找到任何内容时返回 0(成功),并在找到时返回 123(失败)。括号是为了防止有人在 shell 提示符下测试它。带括号它不会在退出时注销,而只是以相同的错误代码退出子shell。

    我用它来快速检查 js 文件的语法:

    find src/js/ -name \*js -exec node \{\} \; 2>&1 | grep -B 5 SyntaxError || exit 0 && exit 1;
    

    【讨论】:

      【解决方案5】:

      您实际上根本不需要使用exit。从逻辑上讲,无论 grep 的结果如何,您的脚本都会退出。由于 shell 脚本的退出值是运行的最后一个命令的退出代码,因此只需将 grep 作为最后一个命令运行,使用 -v 选项反转匹配以更正退出值。因此,您的脚本可以简化为:

      grep -vqP "STATUS: (?!Perfect)" recess.txt
      

      编辑:

      对不起,当文件中有其他类型的行时,上述方法不起作用。不过,为了避免运行多个命令,awk 可以通过以下方式完成整个 shebang:

      awk '/STATUS: / && ! /Perfect/{exit 1}' recess.txt
      

      如果您决定想要 grep 提供的输出,您可以这样做:

      awk '/^STATUS: / && ! /Perfect/{print;ec=1} END{exit ec}' recess.txt
      

      【讨论】:

      • 是的,这是我尝试的第一件事,但这个问题比这更难。你看,通过使用“-v”,它只是返回文件的每一行,如果返回任何东西,它总是会给我一个 grep 中的退出状态“0”。所以不幸的是,这不太符合我的程序的要求。
      • 这就是我添加-q的原因——你的需求列表从未列出你关心grep的输出:)
      • 正确,我不关心输出,但是对于我损坏的文件(添加到问题),使用此命令退出代码仍然是“0”
      • 啊,是的。 -v 的捕获量超出了应有的范围。如果您的主要目标是避免运行多个命令,我已经更新了 grep 的替代方法。
      • 不错!是的,我的主要目标是用一段紧凑的代码来完成这个看似简单的任务。改为点赞。谢谢!不过,我暂时保持开放状态,看看是否有人回答了问题的确切标题,该问题专门询问了 grep。
      【解决方案6】:

      仅否定返回值在 set -e 上下文中不起作用。但你可以这样做:

      ! grep -P "STATUS: (?!Perfect)" recess.txt || false
      

      【讨论】:

        【解决方案7】:

        使用special ? variable

        grep -P "STATUS: (?!Perfect)" recess.txt
        exit $((1-$?))
        

        (但请注意,grep 也可能返回 2,因此不清楚在这种情况下您希望发生什么。)

        【讨论】:

        • 我同意您上面的评论 - 单行并不是特别有用。我真的只是在寻找一种更巧妙的方法来做这件事,看起来应该很容易。我确实在问题中说过我会将其授予最严格的代码。您有一个我不知道的很酷的技巧,但是如果 grep 可以返回状态 2 (我不知道),它似乎有点脆弱。不过谢谢!
        • @SeanAdkinson:没关系,我不是在为投票而战,只是好奇这里的目标是什么;)
        • 要解决 grep 在错误时返回退出代码 2 的问题,您可以执行 exit $(($?^1)),它只翻转最低位。这保留了 2 的错误位,我认为这是您通常想要的。
        【解决方案8】:

        grep 答案的问题在于,如果文件是空的,你也会得到一个干净的响应,就好像文件有一个完美的一样。 所以我个人为此放弃了 grep 并使用了 awk。

        awk 'BEGIN{ef=2}; /STATUS: Perfect/{ ef=0;}; /STATUS: Busted/{ print;eff=3;}; END{exit (ef+eff)}' a.txt ; echo $?
        
        This has exit status:
         0 :  Perfect and !Busted
         2 : !Perfect and  Busted
         3 :  Perfect and  Busted
         5 : !Perfect and !Busted
        

        【讨论】:

        • 很好,我没有考虑空文件的情况。 +1
        【解决方案9】:
        [ $(grep -c -P "STATUS: (?!Perfect)" recess.txt) -eq 0 ]
        

        【讨论】:

        • 你不是说-ne 0吗?
        【解决方案10】:

        我也需要这样的解决方案来编写 puppet only if 语句,并想出了以下命令:

        /bin/grep --quiet 'root: root@ourmasterdomain.de' /etc/aliases; if [ $? -eq 0 ]; then test 1 -eq 2; else test 1 -eq 1; fi;

        【讨论】:

          【解决方案11】:

          既然有人已经发布了 Puppet 解决方案,我不妨添加如何反转 Ansible 运行的 shell 命令:

            - name: Check logs for errors
              command: grep ERROR /var/log/cassandra/system.log
              register: log_errors
              failed_when: "log_errors.rc == 0"
          

          即您只需将失败条件设置为返回代码为 0。因此,如果我们确实在日志中找到单词 ERROR,此命令将失败。

          我选择了这个而不是 grep -v,因为这也会反转 grep 的输出,所以我们会在 log_errors.stdout_lines 中收到我们不想要的所有 DEBUG/INFO/WARN 行。

          【讨论】:

            猜你喜欢
            • 2013-02-10
            • 2018-09-18
            • 2010-10-20
            • 2022-11-10
            • 2014-06-18
            • 2014-06-11
            • 1970-01-01
            • 2011-07-08
            • 1970-01-01
            相关资源
            最近更新 更多