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

时间:2012-08-27 06:53:38

标签: bash shell

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

13 个答案:

答案 0 :(得分:240)

以前,问题是如何检查目录中是否有文件。以下代码实现了这一点,但请参阅rsp's answer以获得更好的解决方案。


空输出

命令不返回值 - 它们输出它们。您可以使用 command substitution 捕获此输出;例如$(ls -A)。您可以在Bash中测试非空字符串,如下所示:

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

请注意,我使用了-A而不是-a,因为它省略了符号当前(.)和父(..)目录条目。

注意:正如评论中所指出的,命令替换不会捕获尾随换行符。因此,如果命令仅输出 换行符,则替换将不会捕获任何内容,并且测试将返回false。尽管不太可能,但在上面的示例中这是可能的,因为单个换行符是有效的文件名!有关详情,请参见this answer


退出代码

如果要检查命令是否成功完成,可以检查$?,其中包含最后一个命令的退出代码(成功为零,失败为非零)。例如:

files=$(ls -A)
if [[ $? != 0 ]]; then
    echo "Command failed."
elif [[ $files ]]; then
    echo "Files found."
else
    echo "No files found."
fi

更多信息here

答案 1 :(得分:74)

TL; DR

if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi

感谢 netj 有关改进原文的建议:
if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi

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

第一个问题

我看到的第一个问题是,此处提供的大多数示例根本无法正常工作。它们使用ls -alls -Al命令 - 两者都在空目录中输出非空字符串。这些示例始终报告有文件,即使没有

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

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

第二个问题

第二个问题是,虽然某些答案工作正常(使用ls -alls -Al,但{{ 1}}代替)他们都做这样的事情:

  1. 运行命令
  2. 将其整个输出缓冲在RAM中
  3. 将输出转换为巨大的单行字符串
  4. 将该字符串与空字符串进行比较
  5. 我建议做的是:

    1. 运行命令
    2. 计算输出中的字符而不存储它们
      • 甚至更好 - 计算最多1个字符的数量ls -A
        (感谢 netj 在下面的评论中发布此想法)
    3. 将该数字与零
    4. 进行比较

      例如,而不是:

      using head -c1

      我会用:

      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
      

      完整示例

      Will Vousden回答的更新示例:

      $ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]
      
      real    0m0.004s
      user    0m0.000s
      sys 0m0.007s
      

      netj

      的建议后再次更新
      if [[ $(ls -A | wc -c) -ne 0 ]]; then
          echo "there are files"
      else
          echo "no files found"
      fi
      

      丢弃空白

      如果您正在测试的命令可以输出一些您想要视为空字符串的空格,那么请改为:

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

      你可以使用:

      | wc -c
      

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

      head -c1

      或类似的东西。

      摘要

      1. 首先,使用工作

      2. 的命令
      3. 其次,避免在RAM中不必要的存储和处理潜在的巨大数据。

      4. 答案并没有指明输出总是很小,因此也需要考虑大输出的可能性。

答案 2 :(得分:32)

if [ -z "$(ls -lA)" ]; then
  echo "no files found"
else
  echo "There are files"
fi

这将运行该命令并检查返回的输出(字符串)是否具有零长度。 您可能需要检查'test' manual pages是否有其他标志。

在正在检查的参数周围使用“”,否则空结果将导致语法错误,因为没有给出第二个参数(要检查)!

注意:ls -la始终返回...,因此使用它不起作用,请参阅ls manual pages。此外,虽然这看似简单方便,但我认为它很容易破解。根据结果​​编写一个返回0或1的小脚本/应用程序更加可靠!

答案 3 :(得分:17)

对于那些想要一个优雅的,独立于bash版本的解决方案(实际上应该在其他现代shell中工作)以及那些喜欢使用单行程来快速完成任务的人。开始了!

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

(请注意,其中一条评论为ls -al,事实上只有-l-a都会返回一些内容,所以在我的回答中我使用简单的ls

答案 4 :(得分:10)

正如Jon Lin所评论的那样,ls -al将始终输出(...)。您希望ls -Al避免这两个目录。

例如,您可以将命令的输出放入shell变量中:

v=$(ls -Al)

较旧的,不可嵌套的符号

v=`ls -Al`

但我更喜欢可嵌套的符号$( ... )

您可以测试该变量是否为非空

if [ -n "$v" ]; then
    echo there are files
else
    echo no files
fi

您可以将两者合并为if [ -n "$(ls -Al)" ]; then

答案 5 :(得分:9)

Bash Reference Manual

6.4 Bash Conditional Expressions

-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

答案 6 :(得分:4)

我猜你想要ls -al命令的输出,所以在bash中,你会有类似的东西:

LS=`ls -la`

if [ -n "$LS" ]; then
  echo "there are files"
else
  echo "no files found"
fi

答案 7 :(得分:2)

这是针对更极端情况的解决方案:

if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi

这将有效

  • 适用于所有Bourne shell;
  • 如果命令输出全为零;
  • 无论输出大小如何都有效;

然而,

  • 一旦输出任何内容,命令或其子进程将被终止。

答案 8 :(得分:0)

到目前为止给出的所有答案都处理命令终止并输出非空字符串。

大多数内容在以下意义上被打破:

  • 他们没有正确处理仅输出换行符的命令;
  • 从Bash≥4.4开始,如果命令输出空字节(因为它们使用命令替换),大多数都会发送标准错误;
  • 大多数都会啜饮完整的输出流,所以会等到命令终止后再回答。有些命令永远不会终止(尝试,例如,yes)。

因此,要解决所有这些问题,并有效地回答以下问题,

  

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

你可以使用:

if read -n1 -d '' < <(command_here); then
    echo "Command outputs something"
else
    echo "Command doesn't output anything"
fi

您还可以添加一些超时,以便使用read的{​​{1}}选项测试命令是否在给定时间内输出非空字符串。例如,超时2.5秒:

-t

备注。如果您认为需要确定命令是否输出非空字符串,则很可能存在XY问题。

答案 9 :(得分:0)

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

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"

答案 10 :(得分:0)

有时你想保存输出,如果它是非空的,则将它传递给另一个命令。如果是这样,你可以使用像

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

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

答案 11 :(得分:0)

mentioned by tripleee in the question comments一样,使用moreutils ifne(如果输入不为空)。

在这种情况下,我们希望{em}否定测试的ifne -n

ls -A /tmp/empty | ifne -n command-to-run-if-empty-input

当初始命令的输出为非空时,它比其他许多答案的优势。 ifne将立即开始将其写入STDOUT,而不是先缓冲整个输出,然后再写入,这对于初始输出缓慢生成或过长并且将溢出Shell变量的最大长度非常重要。 / p>

moreutils中有一些utils可以说应该包含在coreutils中,如果您花费大量时间住在shell中,那么值得一试。

OP可能特别感兴趣的dirempty/exists工具在撰写本文时仍在考虑中,并且已经使用了一段时间(它可能会使用颠簸)。

答案 12 :(得分:0)

有时“某物”可能不是测试应用程序的 stdout 而是 stderr ,因此这是更通用的修复方法:

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