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

时间:2017-03-22 15:54:01

标签: bash shell

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

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

3 个答案:

答案 0 :(得分:4)

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

可以使用set -x

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

答案 1 :(得分:4)

这是因为$( )修剪尾随换行符。从bash reference manual(强调添加):

  

Bash通过在子shell环境中执行命令并使用命令的标准输出替换命令替换来执行扩展,删除任何尾随换行符

您可以通过打印$(cat foo)

的值直接看到此信息
$ echo "'$a'"
'a
'
$ echo "'$(cat foo)'"
'a'

...请注意,对于$( ),结束单引号与“a”在同一行上结束,这意味着在“a”之后没有换行符。此外,

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

请注意,这是与字符串“a”进行比较,后者不包含换行符;而不是变量$a,它确实包含换行符。

答案 2 :(得分:2)

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

您可以这样做将文件数据读回变量:

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=%q\n' "$a" "$b"
a=$'a\n';b=$'a\n'