Bash 为什么不是';当内容(如文件)以换行符结尾时,t[";$(cat文件)";=";$contents";]为true?

Bash 为什么不是';当内容(如文件)以换行符结尾时,t[";$(cat文件)";=";$contents";]为true?,bash,shell,Bash,Shell,发生了什么事?为什么这些不相等?您在测试中比较的变量$a包含一个换行符,而$(cat文件)的结果不包含,因为从命令替换中删除了尾随的换行符 这可以通过使用set-x进行验证: $ a='a ' $ echo -n "$a" | md5sum 60b725f10c9c85c70d97880dfe8191b3 - $ echo -n "$a" > foo $ cat foo | md5sum 60b725f10c9c85c70d97880dfe8191b3 - $ [ "$(cat foo

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

您在测试中比较的变量
$a
包含一个换行符,而
$(cat文件)
的结果不包含,因为从命令替换中删除了尾随的换行符

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

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

您在测试中比较的变量
$a
包含一个换行符,而
$(cat文件)
的结果不包含,因为从命令替换中删除了尾随的换行符

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

$ a='a
'
$ echo -n "$a" | md5sum
60b725f10c9c85c70d97880dfe8191b3  -
$ echo -n "$a" > foo
$ cat foo | md5sum
60b725f10c9c85c70d97880dfe8191b3  -
$ [ "$(cat foo)" == "$a" ] || echo false
false
这是因为
$()
修剪尾随的换行符。从(增加强调)开始:

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

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

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

请注意,这与字符串“a”比较,后者不包含换行符;不是包含换行符的变量
$a

这是因为
$()
修剪尾随的换行符。从(增加强调)开始:

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

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

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


请注意,这与字符串“a”比较,后者不包含换行符;不是包含换行符的变量
$a

命令替换会从文件内容中删除所有后续换行符

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

$ [ "$(cat foo)" = "a" ] && echo true || echo false
true
a='a'
'
#如果BASH>=4,则使用
映射文件arr
命令替换从文件内容中删除所有后续换行符

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

$ [ "$(cat foo)" = "a" ] && echo true || echo false
true
a='a'
'
#如果BASH>=4,则使用
映射文件arr
@arco444,
echo-n“$a”
是否包含换行符?顺便说一句,
=
不能保证与
[
一起工作;POSIX规范中唯一的字符串比较运算符是
=
。请参阅;在
[[[]
中,可以保证有bash从那时起使用的ksh扩展,但是在
[]
您依赖于未定义的行为。另一方面,
echo-n
实际上也不能保证具有您期望的行为。从可移植性的角度来看,使用
printf“%s'”$a“
;请参阅
echo
的规范,特别是应用程序使用和基本原理部分。(Pull quote:“除非同时省略
-n
(作为第一个参数)和转义序列,否则不可能在所有POSIX系统中使用
echo
。)@arco444,
echo-n“$a”
是否包含换行符?顺便说一句,
=
不能保证使用
[
;POSIX规范中唯一的字符串比较运算符是
=
。请参阅;在
[[]]
中,可以保证您拥有bash从那时起使用的ksh扩展,但在
[]
您依赖于未定义的行为。另一方面,
echo-n
实际上也不能保证具有您期望的行为。从可移植性的角度来看,使用
printf“%s'”$a“
;请参阅
echo
的规范,特别是应用程序使用和基本原理部分。(Pull quote:“除非
-n
(作为第一个参数)和转义序列都被省略,否则不可能在所有POSIX系统中使用
进行移植。”)可以使用
=
而不是
=
来提高对基线POSIX
测试
实现的移植性,或者
[[]]
而不是
[]
使对扩展的依赖显式化。@CharlesDuffy:这一点很好;我将其转换为可移植(基线POSIX)语法。@GordonDavisson,有什么方法可以在shell中捕获
foo
文件的全部内容吗?例如,
“$(cat foo)”
但不剥离换行符?@PaulDraper,只要它不包含NUL文本,是的。
IFS=read-r-d''contents@PaulDraper,…*然而*,我真的认为这个问题应该是它自己的问题,单独问,而不是折叠到这个问题中;有人试图找出如何在bash中读取文件而不修剪trailing newlines可能不会注意搜索结果中标题为“shell相等问题”的内容。可能会使用
=
而不是
=
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'