当内容(如文件)以换行符结尾时,为什么 [ "$(cat file)" = "$contents" ] 不成立?


$ a='a
'
$ echo -n "$a" | md5sum
60b725f10c9c85c70d97880dfe8191b3  -
$ echo -n "$a" > foo
$ cat foo | md5sum
60b725f10c9c85c70d97880dfe8191b3  -
$ [ "$(cat foo)" == "$a" ] || echo false
false

发生了什么事情?为什么这些不平等?

这是因为$( )修剪尾随换行符。来自 bash 参考手册(强调添加(:

Bash 通过在 subshell 环境中执行命令并将命令替换替换为命令的标准输出来执行扩展,并删除任何尾随换行符。

您可以通过打印 $(cat foo) 的值直接看到这一点:

$ echo "'$a'"
'a
'
$ echo "'$(cat foo)'"
'a'

。请注意,对于 $( ) ,结束单引号与"A"在同一行结束,这意味着"A"之后没有换行符。也

$ [ "$(cat foo)" = "a" ] && echo true || echo false
true

请注意,这是与不包含换行符的字符串"a"进行比较;而不是包含换行符的变量$a进行比较。

您在

测试中与之进行比较的变量$a包含换行符,而$(cat file)的结果不包含,因为尾随换行符已从命令替换中删除。

这可以通过使用set -x来验证:

[ "$(cat foo)" = "$a" ] || echo false
++ cat foo
+ '[' a = 'a
' ']'
+ echo false

命令替换会从文件内容中删除所有尾随换行符。

您可以执行此操作以将文件数据读回变量:

a='a
'
# if you have BASH >= 4 then use
mapfile arr < foo
IFS=
b="${arr[*]}"
# or else read file content into a variable and append a new line after each read
# b=; while read -r; do b+="$REPLY"$'n'; done < foo

# now compare
[[ $b == $a ]] && echo true || echo false
true
# or check content
declare -p a b
declare -- a="a
"
declare -- b="a
"
# using printf
printf 'a=%q;b=%qn' "$a" "$b"
a=$'an';b=$'an'

相关内容

最新更新