如何在POSIX shell中测试命令输出是否为空?

9
如何在POSIX shell中高效地检查任意命令是否产生了任何输出? (比如说 git ls-files --killed)。有三种明显的方法,但每一种方法看起来都很丑陋或邪恶:
  1. [ -n "$(git ls-files --killed)" ] - 完整的命令输出必须被捕获到内存中并传递给test(至少希望是一个内置命令)。
  2. [ "$(git ls-files --killed | wc -c)" -gt 0 ] - 涉及两个分叉执行器。
  3. TMP=$(tempfile); git ls-files --killed >"$tempfile"; [ -s "$tempfile" ] && ...; rm "$tempfile" - 需要一个中间临时文件(再次捕获所有输出)。
4个回答

4
我偏好的解决方案使用 POSIX shell 内置 read 命令。
if git ls-files --killed | read REPLY; then
  echo "Some output"
else
  echo "No output or git failed"
fi

read 尝试从标准输入中读取一行(并将其存储在变量 REPLY 中),如果成功则返回0,否则返回正值退出代码。管道的其余部分不会被读取。仅适用于bash,如果省略,则默认名称为REPLY,因此read REPLY 可以缩短为read

这种方法的一个潜在问题是未检查所涉及程序的退出代码(例如您的示例中的git)。


4

您也可以检查命令的退出状态。通常,如果命令成功运行,会返回0的退出状态。

git ls-files --killed > /dev/null
if [ $? -eq 0 ]

如果您只想依赖命令的输出,则可以在第一个选项中使用 "head -1",因为看起来您除了知道结果外,并没有对命令输出进行任何处理。


1
git ls-files --killed 命令返回成功,无论它找到多少个文件,就像 ls 命令一样。 - matthewbauer
谢谢!显然,“head -n 1”正是我所缺少的,太完美了。(请注意,我认为您的第一个答案并不那么相关,因为返回/不返回输出与成功/失败非常不同。此外,我认为使用更简洁的版本“if git ls-files --killed >/dev/null; then ...; fi”更加优雅。) - Petr Baudis
(虽然它还涉及两个分支,但我仍然认为这是迄今为止提出的最好的选择。) - Petr Baudis

1
你可能会认为这个解决方案和其他的一样不太正规,但我认为它比其他方案使用的内存更少(我不是shell脚本效率方面的专家)。
代码仅适用于bash:
z=0
while read -r -n1 char; do
  z=1
  break
done < <(git ls-files --killed)
[ $z != 0 ]

它使用临时变量而不是临时文件,我认为read每次只能读取一个字符,但git ls-files命令可能仍会完全执行。它可能不会更少丑陋,但我认为至少更有效率。

1
谢谢,这是个好主意;唯一的大缺点是它非常依赖于Bash。 - Petr Baudis

0

因为您提到了POSIX,我会参考这个答案
我会使用/usr/bin/test,因为它在POSIX系统中是必需的。

/usr/bin/test -n "$(git ls-files --killed)"


很抱歉,我不明白这与什么相关。POSIX 应该保证 plain test / [ 的行为是适当的,而你的解决方案与我的一个相同。 - Petr Baudis
同意。我认为你的第一种解决方案是最好或最有效的,因为它不会创建分支(很可能)并且你不需要输出。 - dougEfresh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接