如何测试命令是否输出空字符串?


当前回答

Bash参考手册

6.4 Bash条件表达式

-z string
     True if the length of string is zero.

-n string
string
     True if the length of string is non-zero.

你可以使用速记版:

if [[ $(ls -A) ]]; then
  echo "there are files"
else
  echo "no files found"
fi

其他回答

适合那些想要优雅的、与bash版本无关的解决方案(实际上应该可以在其他现代shell中工作)以及喜欢使用一行程序执行快速任务的人。我们开始吧!

ls | grep . && echo 'files found' || echo 'files not found'

(注意,正如其中一个评论提到的,ls -al和事实上,只是-l和-a都会返回一些东西,所以在我的回答中,我使用简单的ls

博士TL;

if [[$(ls -A | head -c1 | wc -c) -ne 0]];然后…;fi

感谢netj if [[$(ls -A | wc -c) -ne 0]];然后…;fi


这是一个老问题,但我认为至少有两件事需要改进或至少需要澄清。

第一个问题

我看到的第一个问题是,这里提供的大多数示例根本不起作用。它们使用ls -al和ls -al命令——这两个命令都在空目录中输出非空字符串。这些例子总是报告存在文件,即使没有文件。

出于这个原因,你应该只使用ls - a -为什么有人想要使用-l开关,这意味着“使用长列表格式”,当你想要的只是测试是否有任何输出时,无论如何?

所以大部分答案都是不正确的。

第二个问题

第二个问题是,虽然有些答案很好(那些不使用ls -al或ls -al而是ls -A的答案),但它们都是这样做的:

运行命令 在RAM中缓冲它的全部输出 将输出转换为一个巨大的单行字符串 将该字符串与空字符串进行比较

我的建议是:

运行命令 计算输出中的字符,但不存储它们 或者甚至更好-使用head -c1来计算最多1个字符的数量(感谢netj在下面的评论中发布这个想法) 将这个数字与零进行比较

比如,不用:

if [[ $(ls -A) ]]

我会用:

if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]

而不是:

if [ -z "$(ls -lA)" ]

我会用:

if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]

等等。

对于较小的输出,这可能不是问题,但对于较大的输出,差异可能很大:

$ time [ -z "$(seq 1 10000000)" ]

real    0m2.703s
user    0m2.485s
sys 0m0.347s

比较一下:

$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]

real    0m0.128s
user    0m0.081s
sys 0m0.105s

更好的是:

$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]

real    0m0.004s
user    0m0.000s
sys 0m0.007s

完整的示例

更新的例子来自Will Vousden的回答:

if [[ $(ls -A | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

根据netj的建议再次更新:

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
    echo "there are files"
else
    echo "no files found"
fi

jakeonfire的补充更新:

如果没有匹配,Grep将失败退出。我们可以利用这一点来稍微简化语法:

if ls -A | head -c1 | grep -E '.'; then
    echo "there are files"
fi

if ! ls -A | head -c1 | grep -E '.'; then
    echo "no files found"
fi

丢弃的空白

如果您正在测试的命令可以输出一些空白,您希望将其视为空字符串,那么不要:

| wc -c

你可以用:

| tr -d ' \n\r\t ' | wc -c

或者用head -c1

| tr -d ' \n\r\t ' | head -c1 | wc -c

或者类似的东西。

总结

首先,使用一个有效的命令。 其次,避免在RAM中不必要的存储和处理潜在的巨大数据。

答案并没有说明输出总是很小,所以需要考虑大输出的可能性。

有时“something”可能不是stdout,而是测试应用程序的stderr,所以这里有一个更通用的修复方法:

if [[ $(partprobe ${1} 2>&1 | wc -c) -ne 0 ]]; then
    echo "require fixing GPT parititioning"
else
    echo "no GPT fix necessary"
fi

有时您希望保存输出(如果它是非空的),以便将其传递给另一个命令。如果是的话,你可以使用

list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
    /bin/rm $list
fi

这样,如果列表为空,rm命令就不会挂起。

下面是另一种方法,将某些命令的std-out和std-err写入临时文件,然后检查该文件是否为空。这种方法的一个好处是它捕获两个输出,并且不使用子外壳或管道。后面这些方面很重要,因为它们会干扰捕获bash退出处理(例如这里)

tmpfile=$(mktemp)
some-command  &> "$tmpfile"
if [[ $? != 0 ]]; then
    echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
    echo "Command generated output"
else
    echo "Command has no output"
fi
rm -f "$tmpfile"