当输入不匹配时,防止grep返回错误

Geo*_*nis 64 bash grep

我想在bash脚本中编写一段代码,用于检查程序是否已在运行.我有以下内容,以搜索栏是否正在运行

 foo=`ps -ef | grep bar | grep -v grep`
Run Code Online (Sandbox Code Playgroud)

 grep -v grep
Run Code Online (Sandbox Code Playgroud)

部分是为了确保在ps结果中不考虑"grep bar"

当bar未运行时,foo正确为空.但我的问题在于剧本的事实

 set -e
Run Code Online (Sandbox Code Playgroud)

如果某个命令返回错误,则为终止脚本的标志.事实证明,当bar未运行时,"grep -v grep"与任何内容都不匹配,grep会返回错误.我尝试使用-q或-s但无济于事.

那有什么解决方案吗?谢谢

Sea*_*ean 63

当然:

ps -ef | grep bar | { grep -v grep || true; }
Run Code Online (Sandbox Code Playgroud)

甚至:

ps -ef | grep bar | grep -v grep | cat
Run Code Online (Sandbox Code Playgroud)

  • 第一个是更好的,因为它也适用于"-o pipefail",这是另一个类似于"-e"的bash的快速失败设置. (7认同)
  • 如果`grep`因没有匹配而返回1,那么这很有效,但如果它返回2会发生什么(例如错误)? (5认同)
  • 正如_AndréFratelli_所说,使用`grep $ options || true`忽略了真正的错误!我提出了一个新答案:/sf/answers/3473959961/ (2认同)

gle*_*man 15

要避免的一个好方法grep -v grep是:

ps -ef | grep '[b]ar'
Run Code Online (Sandbox Code Playgroud)

该正则表达式仅匹配字符串"bar".但是在ps输出中,字符串"bar"不会出现在grep进程中.


在我了解之前的几天pgrep,我写了这个函数来自动执行上面的命令:

psg () { 
    local -a patterns=()
    (( $# == 0 )) && set -- $USER
    for arg do
        patterns+=("-e" "[${arg:0:1}]${arg:1}")
    done
    ps -ef | grep "${patterns[@]}"
}
Run Code Online (Sandbox Code Playgroud)

然后,

psg foo bar
Run Code Online (Sandbox Code Playgroud)

变成

ps -ef | grep -e '[f]oo' -e '[b]ar'
Run Code Online (Sandbox Code Playgroud)

  • @ grok12,ps输出中显示的是`grep [b] ar`和*正则表达式*`[b] ar`不能匹配*string*`[b] ar` - 正则表达式将完全匹配3个字符,而字符串包含5个字符 (5认同)

myr*_*rdd 12

简短的回答

ps -ef | grep bar | { grep -v grep || test $? = 1; }
Run Code Online (Sandbox Code Playgroud)

如果你正在使用set -e.

如果使用bash的pipefailoption(set -o pipefail),请记住将异常处理(||test)应用于grep管道中的每个:

ps -ef | { grep bar || test $? = 1; } | { grep -v grep || test $? = 1; }
Run Code Online (Sandbox Code Playgroud)

shell脚本中,我建议你使用"catch-1-grep"(c1grep)实用程序函数:

c1grep() { grep "$@" || test $? = 1; }
Run Code Online (Sandbox Code Playgroud)

解释

grep退出状态为0,1或2:[1]

  • 0 表示选择了一条线
  • 1 表示没有选择任何行
  • 2 表示发生错误

grep如果被信号中断(例如130SIGINT),也可以返回其他代码.

由于我们只想忽略退出状态1,因此我们使用它test来抑制特定的退出状态.

  • 如果grep返回0,test则不运行.
  • 如果grep返回1,test则运行并返回0.
  • 如果grep返回任何其他值,test则运行并返回1.

在最后一种情况下,由于set -eor ,脚本将立即退出set -o pipefail.但是,如果你根本不关心grep错误,你当然可以写

ps -ef | grep bar | { grep -v grep || true; }
Run Code Online (Sandbox Code Playgroud)

正如肖恩所说.


[附加]在shell脚本中的用法

在shell脚本中,如果您经常使用grep,我建议您定义一个实用程序函数:

# "catch exit status 1" grep wrapper
c1grep() { grep "$@" || test $? = 1; }
Run Code Online (Sandbox Code Playgroud)

这样,你的管道将获得短期和简单再次,不失的特点set -eset -o pipefail:

ps -ef | c1grep bar | c1grep -v grep
Run Code Online (Sandbox Code Playgroud)

供参考:

  • 我打电话给它c1grep强调它只是捕捉退出状态1,没有别的.
  • 我可以调用函数grep而不是(grep() { env grep "$@" ...; }),但我更喜欢一个不那么混乱和更明确的名字c1grep.

[1]手册grep


Sor*_*gal 10

如果您只是要扔掉99%的产品,为什么要求ps提供大量的产出-ef呢?ps特别是GNU版本是瑞士军刀的便利功能.试试这个:

ps -C bar -o pid= 1>/dev/null
Run Code Online (Sandbox Code Playgroud)

-o pid=在这里指的只是因为,但事实上它是毫无意义的,因为无论如何我们扔掉了所有的stdout.但是,如果您想知道实际运行的PID,那将非常有用.

ps如果-C无法匹配任何内容,将自动返回非零存在状态,如果匹配,则返回零.所以你可以这么说

ps -C bar 1>/dev/null && echo bar running || echo bar not running
Run Code Online (Sandbox Code Playgroud)

要么

if ps -C bar 1>/dev/null ; then
    echo bar running
else
    echo bar not running
fi
Run Code Online (Sandbox Code Playgroud)

这不是更简单吗?不需要grep,不需要两次甚至一次.