如何同时使用 bash 的 if test 和 find 命令?

如何同时使用 bash 的 if test 和 find 命令?

我有一个包含崩溃日志的目录,并且我想在基于 find 命令的 bash 脚本中使用条件语句。

日志文件以这种格式存储:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

我希望 if 语句仅在有过去 5 分钟内修改过的特定应用程序的崩溃日志时才返回 true。find我将使用的命令是:

find /var/log/crashes -name app-\*\.log -mmin -5

我不知道如何if正确地将其纳入声明中。我认为这可能有效:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

有几个地方我不太明白:

  • 我看过如果标志但我不确定应该使用哪一个(如果有的话)。
  • 我是否需要该test指令,或者我应该直接处理 find 命令的结果,或者可以使用它find... | wc -l来获取行数?
  • 回答这个问题并不是 100% 有必要,但是test是为了测试命令返回的返回代码吗?它们是不可见的 - 在stdout/之外stderr?我阅读了该man页面,但我仍然不清楚何时使用test以及如何调试它。

答案1

[test是同义词([requires除外]),所以你不想使用[ test

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

test如果条件为真,则返回零退出状态,否则返回非零。这实际上可以用任何程序代替来检查其退出状态,其中0表示成功,非零表示失败:

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

但是,上述所有示例仅测试程序的退出状态,而忽略程序的输出。

对于find,您需要测试是否生成了任何输出。-n测试非空字符串:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

help test通过在命令行调用可以获得测试参数的完整列表bash

如果您使用bash(而不是sh),则可以使用[[ condition ]],当您的条件中有空格或其他特殊情况时,它的行为更可预测。否则它通常与使用相同[ condition ]。我[[ condition ]]在这个例子中已经使用了,只要有可能我都会这样做。

我还更改`command`$(command),它的行为通常也类似,但对于嵌套命令更好。

答案2

find如果没有任何错误,将成功退出,因此您不能依靠其退出状态来知道它是否找到任何文件。但是,正如您所说,您可以计算它找到了多少个文件并测试该数字。

它会是这样的:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test(又名[)不检查命令的错误代码,它有一个特殊的语法来进行测试,然后如果测试成功则以错误代码 0 退出,否则以 1 退出。它if检查您传递给它的命令的错误代码,并根据它执行其主体。

请参阅man test(或help test,如果您使用bash) 和help if(同上)。

在这种情况下,wc -l将输出一个数字。我们使用tests 选项-gt来测试该数字是否大于0。如果是,test(或[) 将返回退出代码0if会将退出代码解释为成功,并将在其主体内运行代码。

答案3

这将是

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

或者

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

命令test[ … ]完全相同。唯一的区别是它们的名称,以及[需要结束]作为最后一个参数的事实。与往常一样,在命令替换周围使用双引号,否则命令的输出find将被分解为单词,如果有多个匹配文件(并且当没有参数时,[ -n ]为 true) ,您将收到语法错误,而你想要[ -n "" ]的是错误的)。

在 ksh、bash 和 zsh 中,但在 ash 中则不然,您还可以使用[[ … ]]具有不同解析规则的 which:[是一个普通命令,而[[ … ]]是一个不同的解析结构。你不需要在里面使用双引号[[ … ]](尽管它们不会造成伤害)。您仍然需要;命令后的内容。

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

这可能效率低下:如果 中有很多文件/var/log/crashes,find 将探索所有文件。您应该在找到匹配项后立即或不久后停止查找。使用GNU find(非嵌入式Linux,Cygwin),使用primary -quit

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

对于其他系统,管道find进入head至少在第一次匹配后很快退出(发现管道破裂就会死掉)。

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

head -c 1(如果您的head命令支持,则可以使用。)


或者,使用 zsh。

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then

答案4

find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

是一个合适的解决方案。

-exec service myapp restart ';'导致find调用您想要直接运行的命令,而不需要 shell 来解释任何内容。

-quit导致find在处理命令后退出,从而防止在碰巧有多个文件匹配条件时再次执行该命令。

相关内容