我有一个 shell 脚本,第一行没有指定用于解释命令的 shell 类型。
此 .sh 文件一直在 SCO Unix 5 系统上使用,直到迁移到 RHEL 7 等更现代的系统上为止。
在我看来,sh 是 SCO Unix 中的默认 shell,而 bash 是 Red Hat Linux 中的默认 shell,所以我认为将脚本移植到 linux 并运行它,这个脚本将默认使用 bash 解释。
言归正传,在这个脚本中有这样一个部分:
MY_SETUP=1
echo $MY_SETUP
MY_SETUP=2 export MYSETUP
echo $MY_SETUP
正如您所看到的,导出命令后的变量名称不是赋值中的变量名称(这是一个输入错误)。
我注意到,如果脚本由 sh 或 bash 解释,则本节之后的 MY_SETUP 变量的值会有所不同。
sh MY_SETUP 值 = 2
bash MY_SETUP 值 = 1
bash 似乎完全忽略了与导出命令内联的赋值并保留以前的值。
所有这些运行都没有返回任何错误,所以我想知道为什么会有不同的行为。有人能给我解释一下吗?
编辑:
从 Stéphane Chazelas 的回答看来,在 bash 中这条指令
var=x export var
不设置“x”值并且不导出它,但在我的环境中它两者兼而有之。我很困惑。
答案1
您似乎检测到了 POSIX 偏差,bash
这也是与历史悠久的 Bourne Shell 的偏差。您可以将其称为错误或只是偏离行为。
您引用的脚本打印
1
2
与默认行为中除 bash 之外的所有 shell 一起使用。
如果你打电话bash --posix
,它就能正常工作。
从用户 Kusalananda 的指针看来,bash 默认情况下使所有内置命令在退出时恢复其临时环境,而不仅仅是非特殊内置命令。由于export
它是一个特殊的内置程序,POSIX 要求 shell 的行为与 20 世纪 80 年代初的 Bourne Shell 相同,以保持环境值。
由于 bash 默认情况下不实现这一点,因此您会得到偏差。
答案2
是的,行为不同。整个描述并不那么简单。
第一:什么时候相等?
此代码行:
$ var=1; printf "%s" "$var"; var=2 export var; echo " $var"
将1 2
在所有(非 csh 之类)shell 中打印除了嘘。
jsh : 1 2 # ATT version sh (heirloom).
ash : 1 2
yash : 1 2
dash : 1 2
zsh/sh : 1 2
bash : 1 2
posixbash : 1 2
lksh : 1 2
mksh : 1 2
ksh93 : 1 2
attsh : 1 2
zsh : 1 1
对我来说这看起来像是 zsh 的错误。导出变量不保留导出的值怎么可能合理?
但这是因为导出的变量:var
与打印的 var 相同。
导出一些其他变量。
如果该行更改为与您所要求的更相似的内容,并带有其他一些变量名,如下所示:
$ var=1; printf "%s" "$var"; var=2 export othervar; echo " $var"
差异变得清晰:
jsh : 1 2
dash : 1 2
bash : 1 1
posixbash : 1 2
ksh93 : 1 2
zsh : 1 1
很明显,bash 与旧的 sh (Bourne)、新的 sh (dash)、ksh 和其他(此处未列出)不同。
但更重要的是 bash 的行为与bash --posix
.
Posix 要求。
一些 shell 内置函数(不是全部)被称为“特殊内置函数”:
shell 命令语言应支持以下“特殊内置”实用程序。 …但是,此处描述的特殊内置实用程序在两个方面与常规内置实用程序不同:
特殊内置实用程序中的错误可能会导致执行该实用程序的 shell 中止,而常规内置实用程序中的错误不应导致执行该实用程序的 shell 中止。 ……
如简单命令中所述,调用特殊内置实用程序之前的变量赋值在内置实用程序完成后仍然有效;常规内置实用程序或其他实用程序不会出现这种情况。
因此,在一个简单的命令中:var=2 specialBuiltin
变量var
应该在specialBuiltin退出后保留其值。但并非所有 shell 实现都遵循这样的规则。
所以,这应该打印1 hello 2
(就像eval
一个特殊的内置函数一样):
var=1; printf '%s ' "$var"; var=2 eval printf %s hello; echo " $var"
它在sh
和bash --posix
但不是在普通的 bash 中。
特殊内置函数列表。
事实上,POSIX 特殊内置列表在这里,列表为:
02 break
03 :
04 .
05 continue
06 eval
07 exec
08 exit
09 export
10 readonly
11 return
12 set
13 shift
14 times
15 trap
16 unset
第一列中的数字是每次测试使用的 var 值。
我们可以使用以下代码测试所有特殊的内置函数(除了 exit、exec 和 times):
var=01;
while : ; do var=02 break; done; printf ' %s' "02-$var"; var=01
var=03 : ; printf ' %s' "03-$var"; var=01
echo 'printf " %s" "04-<$var>"' >source-sh
var=04 . source-sh; printf ' %s' "04-$var"; var=01
c=0; while ((c++<1)); do
var=05 continue
done; printf ' %s' "05-$var"; var=01
var=06 eval 'printf " %s" "06-<$var>"'; printf ' %s' "06-$var"; var=01
#( var=07 exec bash -c 'printf " %s" "07-$var"'); var=01
#( var=08 exit; printf ' %s' "08-$var" ); var=01
var=09 export var; printf ' %s' "09-$var"; var=01
var=10 readonly i; printf ' %s' "10-$var"; var=01
varfun(){ var=11 return; }; varfun; printf ' %s' "11-$var"; var=01
var=12 set -- aa ; printf ' %s' "12-$var"; var=01
var=13 shift; printf ' %s' "13-$var"; var=01
var=15 trap; printf ' %s' "14-$var"; var=01
var=16 unset j; printf ' %s' "15-$var"; var=01
echo
要打印此列表:
jsh : 02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
dash : 02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
bash : 02-01 03-01 04-<04> 04-01 05-01 06-<06> 06-01 09-09 10-01 11-01 12-01 13-01 15-01 16-01
posixbash : 02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
ksh93 : 02-02 03-03 04-<04> 04-04 05-05 06-<06> 06-06 09-09 10-10 11-11 12-12 13-13 15-15 16-16
zsh : 02-01 03-01 04-<04> 04-01 05-01 06-<06> 06-01 09-01 10-01 11-01 12-01 13-01 15-01 16-01
正如您所看到的,在 posix 要求 var 保留的情况下,02
大多数 shell 保留它并打印02-02
,但在 bash (或 zsh)中则不保留,因为它们打印02-01
。仅在export
bash 打印09-09
和 zsh 打印中09-01
。