如何使用单个命令检查“ls”是否输出某些内容?

如何使用单个命令检查“ls”是否输出某些内容?

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

我成功获取了111or 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 {} +,但如果您有这些,您还有其他更好的选择。)

相关内容