我有一个包含崩溃日志的目录,并且我想在基于 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
将输出一个数字。我们使用test
s 选项-gt
来测试该数字是否大于0
。如果是,test
(或[
) 将返回退出代码0
。if
会将退出代码解释为成功,并将在其主体内运行代码。
答案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
在处理命令后退出,从而防止在碰巧有多个文件匹配条件时再次执行该命令。