如果一起测试和查找命令,如何使用bash的?


25

我有一个包含崩溃日志的目录,并且我想在基于查找命令的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

我不清楚一些地方:

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

一般情况的真正答案是使用find ... -exec。另请参阅“ 为什么循环遍历find的输出错误做法”
通配符

@Wildcard-不幸的是,不能解决一般情况:如果有多个匹配项,则该项不起作用,并且该动作仅需运行一次;如果有一个匹配项,则该动作不起作用无匹配。前者可以使用来解决... -exec command ';' -quit,但我认为除了解析结果外,没有其他解决方案。同样,在这两种情况下,解析结果的主要问题find(即无法将分隔符与文件名中的字符区分开)都不适用,因为在这些情况下您无需查找分隔符。
Jules

Answers:


27

[并且test是同义词(除[需要]),所以你不希望使用[ test

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

test如果条件为true,则返回零退出状态,否则返回非零。实际上,可以用任何程序替换它来检查其退出状态,其中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 testbash命令行中调用来获得测试参数的完整列表。

如果您正在使用bash(而不是sh),则可以使用[[ condition ]],当您的条件中存在空格或其他特殊情况时,它的行为更具预测性。否则,它通常与使用相同[ condition ]。我已[[ condition ]]在本示例中使用过,如可能的话。

我还更改`command`$(command),通常也具有类似的行为,但是使用嵌套命令会更好。


echo可能失败:尝试echo 'oops' > /dev/full
derobert

这个答案绕开了问题的根源,但是优雅地避免了确切地提到问题所在。
bahamat 2012年

9

find如果没有任何错误,它将成功退出,因此您不能指望它的退出状态来知道它是否找到任何文件。但是,正如您所说,您可以计算找到的文件数量并测试该数量。

就像这样:

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

test(aka [)不检查命令的错误代码,它具有进行测试的特殊语法,如果测试成功,则以错误代码0退出,否则退出1。它是if检查传递给它的命令的错误代码,并根据它执行其主体的程序。

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

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


1

这是

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中不是,您还可以使用[[ … ]]具有不同解析规则[的命令:是一个普通命令,而又[[ … ]]是一个不同的解析结构。您不需要在其中使用双引号[[ … ]](尽管它们不会造成伤害)。您仍然需要;after命令。

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

这可能效率不高:如果中有很多文件/var/log/crashes,find将会浏览所有文件。找到匹配项后或之后,您应该立即停止搜索。对于GNU find(非嵌入式Linux,Cygwin),请使用-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

1

这应该工作

if find /var/log/crashes -name 'app-\*\.log' -mmin -5 | read
then
  service myapp restart
fi

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

在这里是一个合适的解决方案。

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

-quit导致find在处理命令后退出,从而如果碰巧有多个符合条件的文件,则阻止再次执行该命令。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.