在 bash 中测试 "tail -c 1"

Testing "tail -c 1" in bash

中,一些帖子使用[[ ]]==来比较字符。

我想写一个单行命令来检测EOF处是否没有换行,我遇到了这个小问题。

echo的输出中,最后有\n

$ echo echo | od -c           
0000000   e   c   h   o  \n
0000005
$ echo -n echo | od -c           
0000000   e   c   h   o
0000004

如果我将 [[ ]]== 放在一起,那么我不会得到预期的输出。

$ [[ `echo echo | tail -c1` == "\n" ]] && echo true
$ [[ `echo echo | tail -c1` != "\n" ]] && echo true
true
$ [[ `echo -n echo | tail -c1` != "\n" ]] && echo true
true

od -c所示,echo echo | tail -c1的输出是\n,而[[ "\n" == "\n" ]] && true会returntrue,所以我预计第一个命令给出 true。但是,为什么它被评估为空字符串?

感谢阅读!

正如Bash Reference Manual中明确指出的那样:

Bash performs the expansion by executing command in a subshell environment and replacing the command substitution with the standard output of the command, with any trailing newlines deleted.

打开-x标志,看看会发生什么:

$ set -x
$ [[ `echo echo | tail -c 1` == '\n' ]]
++ tail -c 1
++ echo echo
+ [[ '' == \\n ]]
$
$ echo "$(echo)"
++ echo
+ echo ''

作为旁注,即使尾随的换行符没有被修剪,你的比较也不会 return 正确,因为 '\n' 不是换行符,而是字面上的反斜杠后跟字母n。您应该使用 $'\n' 来获得实际的换行。

至于解决方案,您可以使用以下方法:

$ printf 'test\n' | perl -0777ne'exit(/\n\z/?0:1)' || echo "Missing line feed" >&2

$ printf 'test'   | perl -0777ne'exit(/\n\z/?0:1)' || echo "Missing line feed" >&2
Missing line feed

$ printf 'test\n' | perl -0777ne'die("Missing line feed\n") if !/\n\z/'

$ printf 'test'   | perl -0777ne'die("Missing line feed\n") if !/\n\z/'
Missing line feed