如何测试命令是否输出空字符串?
13 回答
之前的问题是问如何查看目录下是否有文件。以下代码实现了这一点,但请参阅rsp 的答案以获得更好的解决方案。
空输出
命令不返回值——它们输出它们。您可以使用命令替换来捕获此输出;例如$(ls -A)
。您可以像这样在 Bash 中测试非空字符串:
if [[ $(ls -A) ]]; then
echo "there are files"
else
echo "no files found"
fi
请注意,我使用了-A
而不是-a
,因为它省略了符号 current ( .
) 和 parent ( ..
) 目录条目。
注意:正如评论中指出的那样,命令替换不会捕获尾随换行符。因此,如果命令仅输出换行符,则替换将不捕获任何内容,并且测试将返回 false。虽然不太可能,但在上面的示例中这是可能的,因为单个换行符是一个有效的文件名!此答案中的更多信息。
退出代码
如果要检查命令是否成功完成,可以检查$?
,其中包含最后一个命令的退出代码(成功为零,失败为非零)。例如:
files=$(ls -A)
if [[ $? != 0 ]]; then
echo "Command failed."
elif [[ $files ]]; then
echo "Files found."
else
echo "No files found."
fi
更多信息在这里。
TL;博士
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi
感谢netj
提出改进我原来的建议:if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi
这是一个老问题,但我看到至少有两件事需要改进或至少需要澄清。
第一个问题
我看到的第一个问题是这里提供的大多数示例根本不起作用。他们使用ls -al
andls -Al
命令——这两个命令都在空目录中输出非空字符串。这些示例总是报告即使没有文件也有文件。
出于这个原因,你应该只使用ls -A
- 当你想要的只是测试是否有任何输出时,为什么有人要使用-l
表示“使用长列表格式”的开关?
所以这里的大多数答案都是不正确的。
第二个问题
第二个问题是,虽然有些答案可以正常工作(那些不使用ls -al
或相反的答案),ls -Al
但ls -A
它们都做这样的事情:
- 运行命令
- 在 RAM 中缓冲其整个输出
- 将输出转换成一个巨大的单行字符串
- 将该字符串与空字符串进行比较
我建议做的是:
- 运行命令
- 计算其输出中的字符而不存储它们
- 甚至更好 - 计算最多 1 个字符的数量
using head -c1
(感谢netj在下面的评论中发布这个想法)
- 甚至更好 - 计算最多 1 个字符的数量
- 将该数字与零进行比较
因此,例如,而不是:
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 中进行不必要的存储和处理潜在的巨大数据。
答案没有说明输出总是很小,因此也需要考虑大输出的可能性。
对于那些想要一个优雅的、独立于 bash 版本的解决方案的人(实际上应该在其他现代 shell 中工作)以及那些喜欢使用单行代码来完成快速任务的人。开始了!
ls | grep . && echo 'files found' || echo 'files not found'
(注意作为提到的评论之一,ls -al
事实上,只是-l
并且-a
都会返回一些东西,所以在我的回答中我使用简单ls
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
正如 Jon Lin 所说,ls -al
将始终输出 (for .
and ..
)。你想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
有时,ls
可能是一些 shell别名。您可能更喜欢使用$(/bin/ls -Al)
. 请参阅ls(1)和hier(7)和environ(7)以及您的~/.bashrc
(如果您的外壳是GNU bash;我的交互式外壳是zsh,定义在/etc/passwd
- 请参阅passwd(5)和chsh(1))。
我猜你想要ls -al
命令的输出,所以在 bash 中,你会有类似的东西:
LS=`ls -la`
if [ -n "$LS" ]; then
echo "there are files"
else
echo "no files found"
fi
有时“某些东西”可能不会出现在标准输出中,而是出现在测试应用程序的标准错误中,所以这里是更通用的修复方法:
if [[ $(partprobe ${1} 2>&1 | wc -c) -ne 0 ]]; then
echo "require fixing GPT parititioning"
else
echo "no GPT fix necessary"
fi
这是针对更极端情况的解决方案:
if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi
这将起作用
- 对于所有 Bourne 贝壳;
- 如果命令输出全为零;
- 无论输出大小如何,都有效;
然而,
- 一旦输出任何内容,该命令或其子进程将被终止。
到目前为止给出的所有答案都涉及终止并输出非空字符串的命令。
大多数在以下意义上被破坏:
- 它们不能正确处理仅输出换行符的命令;
- 从 Bash≥4.4 开始,如果命令输出空字节(因为它们使用命令替换),大多数将垃圾邮件标准错误;
- 大多数会啜饮完整的输出流,所以会等到命令终止后再回答。有些命令永远不会终止(尝试,例如,
yes
)。
因此,要解决所有这些问题,并有效地回答以下问题,
如何测试命令是否输出空字符串?
您可以使用:
if read -n1 -d '' < <(command_here); then
echo "Command outputs something"
else
echo "Command doesn't output anything"
fi
read
您还可以使用's-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 问题。
这是另一种方法,它将某些命令的 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"
有时您想保存输出,如果它是非空的,则将其传递给另一个命令。如果是这样,你可以使用类似的东西
list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
/bin/rm $list
fi
这样,rm
如果列表为空,命令将不会挂起。
正如triplee 在问题评论中提到的,使用moreutils ifne
(如果输入不为空)。
ifne -n
在这种情况下,我们想要否定测试:
ls -A /tmp/empty | ifne -n command-to-run-if-empty-input
当初始命令的输出为非空时,这优于许多其他答案。ifne
将立即开始将其写入 STDOUT,而不是缓冲整个输出然后再写入,如果初始输出生成缓慢或非常长并且会溢出 shell 变量的最大长度,这一点很重要。
moreutils 中有一些实用程序可以说应该在 coreutils 中——如果你花很多时间住在 shell 中,它们值得一试。
OP 特别感兴趣的可能是dirempty/exists
在撰写本文时仍在考虑中的工具,并且已经有一段时间了(它可能会使用凹凸)。