TL/DR:我在 Solaris 10 中工作。我有一个ls ... | egrep ...
命令,我需要知道它是否输出任何结果。我可以| wc -c
在末尾添加一个;但我需要结果(0 或非零)位于退出代码中,而不是输出中。而且我不能使用if
,它不是 bash 脚本,我只能执行一个命令。
长版:我正在编写一个维护过程来压缩和删除 Solaris 10 系统中的旧日志文件。它检查给定路径内的所有 .log 或 .xml 文件,获取给定月份最后修改的文件,用它们创建 .tar,然后删除原始文件:
ls -Egopqt /path/ | egrep -i '2016-10-[0123][0-9] .*(\.log$|\.xml$)' | awk '{ print $7 }'
| xargs tar -cvf target.tar
删除文件的方法相同,只需将最后一部分替换为:| xargs -i rm {}
我可能把它复杂化了,但是有用。除非没有文件对于给定的月份;如果是这样的话,我会收到一条错误消息tar: Missing filenames
。在尝试创建 tar 之前如何检查它?我想到了这样的事情使用 wc 检查是否有输出:
ls ... | egrep ... | wc -c
0
当没有任何文件时,它会正确输出,否则会输出另一个数字。问题是:我看不到输出,只有退出代码(由于没有错误,因此始终为 0)。我不是在 bash 脚本中执行此操作,而是使用 Siebel CRM:我有一个 javascript 函数,可以生成命令并使用以下命令执行它们库系统来电。我唯一能看到的是退出代码:0
表示正常,非零表示错误(我看到的是实际数字,而不是“非零”)。
以前我有一个类似的要求,检查是否单身的文件是否存在,以及这个答案帮助我做到了这一点:
[ -f filename ] && exit 111 || exit 0
我成功获取了111
or 0
,具体取决于文件名是否存在。但我无法让它与ls ... | egrep ... | wc
命令一起工作。我尝试过使用这个语法:
[[ $( ls -Egopq /path/ | egrep -i ... | wc -c ) -ne 0 ]] && exit 111 || exit 0
但我总是退出代码2,无论是否有文件都没关系。我究竟做错了什么?
PS:我知道我可以编写一个小 shell 脚本来执行检查,使用简单的方法if
来比较输出,然后返回我想要的任何退出代码。还有,我其实能访问命令的输出,我只需将其重定向到 a > tempfile
,然后从 Siebel 读取它。但是,我宁愿避免这两个选项,因为我试图避免创建不必要的(临时或永久)文件。
答案1
egrep
如果没有匹配的行,则返回非零。
答案2
来自xargs
手册:
--no-run-if-empty
-r If the standard input does not contain any nonblanks, do not run the command.
Normally, the command is run once even if there is no input. This option is a GNU extension.
看来这样做 ... | xargs -r tar -cvf target.tar
应该能解决问题!
答案3
该ls
命令针对向用户呈现信息进行了微调,这意味着它不太适合生成可可靠解析的输出。
考虑使用find
代替,这意味着您也可以摆脱它grep
。
例如,删除所有名称结尾.DeleteMe
且超过一周的文件:
find /path -name '*.DeleteMe' \
-mtime +6 \
-exec rm -f -- {} +
如果您想查看它将做什么,请替换-exec rm
为-exec echo rm
.
与任何涉及 (*1) 的方法不同xargs
,此方法的优点是,如果遇到包含奇怪字符的文件名,它不会做奇怪的事情。
如果您想在出现以下情况时采取其他操作不匹配文件,将文件列表写入临时文件,然后检查该文件是否为空就足够简单了。
find /path -name '*.DeleteMe' \
-mtime +6 \
-exec rm -f -- {} + \
-print > list_of_files$$
if ! [ -s list_of_files$$ ] ; then
echo There were no matching files
fi
rm -f list_of_files$$
如果您需要在第一个文件之前执行一些附加操作,那么
find /path -name '*.DeleteMe' \
-mtime +6 \
-exec sh -c '
o=$0 # at_least_one$$ from the outer shell
[ -e $o ] || do_before_stuff
> $o
for f do
do_something with "$f"
done
' at_least_one$$ {} +
if [ -e at_least_one$$ ] ; then
do_after_stuff
else
do_no_files_stuff
fi
rm -f at_least_one$$
这当然是一个简化的例子。如果您正在做任何复杂的事情,那么sh -c 'code here'
请考虑编写一个脚本并调用它,而不是。
对于你的具体问题我会写:
find /path/ -name '2016-10-[0123][0-9]*' \
\( -name '*.log' -o -name '*.xml' \) \
-exec tar -cvf target.tar {} +
我注意到,这与迄今为止显示的所有其他方法具有相同的故障模式:如果文件列表对于命令行来说太长,tar
则会重复调用,这可能会导致 tarball 只包含最后几个文件。
为了解决这个问题,我建议使用cpio
:
find /path/ -name '2016-10-[0123][0-9]*' \
\( -name '*.log' -o \
-name '*.xml' \) \
-print |
cpio -o > target.cpio.trial
if [ -s target.cpio.trial ]
then mv target.cpio.trial target.cpio
else rm target.cpio.trial
fi
部分版本cpio
支持-H格式,这意味着您可以告诉它编写一个可以使用tar
;提取的 tarball否则,您需要cpio
在需要时提取文件(除非您的版本tar
可以理解 cpio 格式的存档)。
(脚注 *1:好的,如果您有 GNU find 和 GNU xargs,那么find ... -print0 | xargs -0r cmd ...
与 一样可靠find ... -exec cmd {} +
,但如果您有这些,您还有其他更好的选择。)