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


237

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


7
命令不返回字符串(它返回一个小的整数退出码,通常为0表示成功,而失败则为1或2),但是它可能会输出一些数据以放入字符串中。
Basile Starynkevitch 2012年

@BasileStarynkevitch这就是我需要的东西。我知道命令返回退出代码。但是我的命令退出代码始终为0。所以我需要控制输出
barp 2012年

1
您应该阅读一些Bash脚本教程tldp.org/LDP/abs/html
Basile Starynkevitch 2012年

Joey Hess ifne为此提供了实用程序。joeyh.name/code/moreutils
Tripleee,

Answers:


309

以前,该问题询问如何检查目录中是否有文件。以下代码可以实现这一点,但是请参见rsp的答案以获得更好的解决方案。


空输出

命令不返回值,而是输出它们。您可以使用命令替换捕获此输出;例如$(ls -A)。您可以像这样在Bash中测试非空字符串:

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

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

注意:注释中所指出,命令替换不会捕获结尾的换行符。因此,如果命令输出换行符,则替换将不捕获任何内容,并且测试将返回false。虽然不太可能,但这在上面的示例中是可能的,因为单个换行符是有效的文件名!有关此答案的更多信息。


退出码

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

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

更多信息在这里


4
可以省略 -n,因此只是if [[ $(ls -A) ]]; then
用户

6
对于仅输出换行符的命令,此方法为false。
西罗Santilli郝海东冠状病六四事件法轮功

89

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而是使用的答案ls -A),但它们都可以执行以下操作:

  1. 运行命令
  2. 将其整个输出缓冲在RAM中
  3. 将输出转换成巨大的单行字符串
  4. 比较那个字符串和一个空字符串

我建议做的是:

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

因此,例如,代替:

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

或类似的东西。

摘要

  1. 首先,用命令的作品

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

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


3
[[ $(... | head -c | wc -c) -gt 0 ]]还是[[ -n $(... | head -c1) ]]更好,因为wc -c必须消耗命令的整个输出。
netj

@netj这是一个非常好的主意。查看我的最新答案-我添加了您的建议。非常感谢!
rsp

关于如何获得输出的任何想法?
fahrradflucht

我认为在一般情况下,原件(无头)会更好。在开始写输出后立即终止该命令并不总是一个好主意!
stk

@stk大多数程序在收到终止信号后将安全退出。
凌晨

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

这将运行命令并检查返回的输出(字符串)的长度是否为零。您可能需要检查“测试”手册页中是否有其他标志。

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

注意:ls -la总是返回...因此使用它将不起作用,请参见ls手册页。此外,尽管这看起来很方便且容易,但我想它会很容易损坏。编写一个小脚本/应用程序,根据结果返回0或1更加可靠!


您可能更喜欢使用$(而不是反引号,因为$(嵌套效果更好
Basile Starynkevitch 2012年

没错,最好始终使用它们,尽管我们不需要在此处嵌套。
Veger 2012年

23

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

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

(请注意,作为提及的评论之一,ls -al实际上,正义-l和正义-a都会返回一些东西,因此在我的回答中,我使用简单ls


5
如果grep -q ^改用,换行符也将匹配,并且grep不会在标准输出中显示任何内容。此外,它将在收到任何输入后立即退出,而不是等待其输入流结束。
mortehu 2015年

与应该开始ls -A,如果你想包括点文件(或目录)
wrlee

13

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

1
括号[[...]]中缺少-z或-n。
2015年

4
@ 71GA:也许这很容易忽略,但是如果仔细看,您会发现这只是string的同义词-n string
用户

10

正如乔恩·林(Jon Lin)所说,ls -al将始终输出(for ...)。您要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

我猜想您想要ls -al命令的输出,因此在bash中,您将拥有类似以下内容的东西:

LS=`ls -la`

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

这是行不通的:该命令ls永远不会执行。您仅检查变量扩展是否LS为非空。
gniourf_gniourf

1
@gniourf_gniourf-是什么让你觉得呢?反引号当然不如$()漂亮或灵活,但它的工作原理是…
tink

-a交换机将不会返回任何内容(即是否有文件或不将返回的内容),因为总有隐性...目录存在。
wrlee

3

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

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

这会起作用

  • 对于所有伯恩贝壳;
  • 如果命令输出全为零;
  • 高效地与输出大小无关;

然而,

  • 一旦输出任何命令,该命令或其子进程将被杀死。

1

鉴于到目前为止处理所有的答案是命令终止并输出一个非空字符串。

大多数在以下方面被打破:

  • 它们不能正确处理仅输出换行符的命令;
  • 从Bash≥4.4开始,如果命令输出空字节(因为它们使用命令替换),则大多数将发送垃圾邮件标准错误;
  • 大多数将吞噬整个输出流,因此将等待直到命令终止才应答。有些命令永远不会终止(例如尝试yes)。

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

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

您可以使用:

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

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

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

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


我认为这个答案很被低估。我在性能测试的一些解决方案的使用这里100次迭代的每3个输入场景(seq 1 10000000seq 1 20,和printf"")。这种读取方法唯一不能赢得的比赛是-z "$(command)"空输入的方法。即使那样,它只会损失大约10-15%。他们似乎收支平衡seq 1 10。无论如何,随着输入大小的增加,该-z "$(command)"方法变得如此缓慢,以至于我会避免将其用于任何可变大小的输入。
abathur

0

这是一种替代方法,该方法将某些命令的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"

0

有时,您想要保存输出(如果它是非空的),以将其传递给另一个命令。如果是这样,您可以使用类似

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.