这里的文档和 POSIX shell 中的只读标志和命令替换

here document and the readonly flag and command substitution in POSIX shells

以下文本在 POSIX 模式下受到两个 Unix shells 的不同处理:

readonly a=$(cat - <<'EOF'
1st
2nd
EOF
)

一个shell (bash(1) --posix) 使得$a有“1st 2nd”而另一个拒绝,说

dash: 1: readonly: 2nd: bad variable name

一旦 readonly 被删除,即当 a 是分配给 RHS 的新变量时,shell 就一致。

其中一个 shell 比另一个更正确吗?

使用shell,我可以先赋值a,然后标记readonly,这样就有解决办法了。尽管如此,当移植到 dash(1)(版本 0.5.7-4ubuntu1)时,重写似乎是一个要求。这是 dash 中的错误吗?

更新:从chepner的回答(文字,引用)中学习,似乎值得一提的是在此过程中发现的一些问题。因此,

readonly a=$*             ; echo a $a
b=$*         ; readonly b ; echo b $b
readonly c="$*"           ; echo c $c

在所有 bashdashposh 中产生不同的行为, zsh, 包括根据 $* 中文本的错误。通过测试将 readonly 换成 local 时出现类似问题。

TL;DR 这可能是未定义行为的情况,其中 shell 可以自由地将 readonlyname=value 参数视为真正的赋值语句或常规字符串参数。这将影响分词应用于值的方式。


我认为这是 dash 正确的情况。在正常赋值中,例如

a=$(cat - <<'EOF'
1st
2nd
EOF
)

右侧不受分词影响。命令替换的结果是一个带有两个嵌入换行符的字符串,它被分配给变量。

然而,

readonly本身就是一个命令; name=value 语法是普通参数,而不是赋值语句,因此会受到分词的影响。命令替换像以前一样生成一个带有嵌入换行符的字符串,但由于它没有被引用,这些换行符被视为任意空格。结果相当于

readonly a=1st 2nd

这显然是错误的。如您所料,如果您引用命令替换(保护换行符),您将获得预期的分配:

readonly a="$(cat - <<'EOF'
1st
2nd
EOF
)"

zsh 产生与 dash:

相同的错误
% readonly a=$(cat - <<'EOF'
cmdsubst heredoc> 1st
cmdsubst heredoc> 2nd
cmdsubst heredoc> EOF
cmdsubst> )
readonly: not an identifier: 2nd
192%

bashksh 表现出相同的行为。 bash 手册页没有给出任何关于为什么不需要引用的提示,尽管 ksh 手册页确实暗示 readonly 的参数是一个真正的赋值(强调我的):

Variable Assignments.

One or more variable assignments can start a simple command or can be arguments to the typeset, enum, export, or readonly special built-in commands