这始于Bourne外壳的破解。在Bourne Shell中,对列表上下文中的所有单词(命令行参数或for循环循环的单词)进行了IFS单词拆分(在标记化之后)。如果你有:
IFS=i var=file2.txt
edit file.txt $var
这第二条线将在3个字被标记化,$var将扩大和拆分+水珠会在所有三个词来完成的,所以你最终会运行ed有t,f,le.txt,f,le2.txt作为参数。
引用其中的一部分将防止split + glob。Bourne Shell最初通过在内部设置第8位来记住哪些字符被引用(后来在Unix变为8bit clean时发生了变化,但是Shell仍然执行类似的操作以记住引用了哪个字节)。
二者$*并$@分别将位置参数的级联与-之间的空间。但是对$@双引号内的when 进行了特殊处理。如果$1包含foo bar和$2包含baz,"$@"则将扩展为:
foo bar baz
^^^^^^^ ^^^
(与 ^上面 s指示哪个字符设置了第8位)。引用第一个空格的位置(设置了第8位),但是没有引用第二个空格的位置(在单词之间添加了空格)。
而且是IFS拆分负责分隔参数(假设$IFS默认情况下使用空格字符)。这类似于$*其前身Mashey shell(本身基于Thomson shell,而Bourne shell是从头开始编写)进行扩展的方式。
这就解释了为什么在Bourne Shell中最初"$@"会在位置参数列表为空(您必须使用来解决${1+"$@"})时将其扩展为空字符串而不是什么都不会扩展,为什么它不保留空的位置参数,以及为什么"$@"没有什么时候不工作$IFS不包含空格字符不起作用。
目的是能够逐字传递参数列表到另一个命令,但这不适用于空列表,空元素或$IFS不包含空格的情况(前两个问题最终在更高版本中得到了解决。 )。
Korn外壳程序(POSIX规范所基于的外壳程序)以几种方式更改了该行为:
- IFS拆分仅针对未加引号的扩展结果(不适用于上面的示例中的
edit或file.txt)
$*并且在为空时$@以$IFS或空格的第一个字符进行连接,$IFS除了对于带引号的"$@",该连接器像在Bourne shell中一样是不带引号的;对于带引号的"$*"when IFS为空,则附加位置参数而没有分隔符。
- 它增加了对阵列,并用
${array[@]} ${array[*]}让人想起伯恩的$*和$@,但在指数之0开始的1代替,以及稀疏(更像关联数组),这意味着$@不能真正被视为ksh的阵列(与比较csh/ rc/ zsh/ fish/ yash哪里$argv/ $*是正常数组)。
- 空元素将保留。
"$@"when $#为0时现在扩展为空字符串而不是空字符串,仅当为空"$@"时$IFS,不包含空格时有效IFS。$*如果$IFS为空,则不带通配符的无引号会扩展为一个参数(位置参数与空格连接)。
ksh93解决了上面剩下的一些问题。在ksh93中,$*并$@扩展到位置参数列表,而不考虑的值而分开$IFS,然后在列表上下文中进一步进行split + globbed + brace-expanded ($*与的第一个字节(非字符)连接)$IFS,"$@"在列表上下文中扩展至列表的位置参数,与的值无关$IFS。在非列表上下文中,例如in var=$@,$@都与空格连接,而与的值无关$IFS。
bash的数组是在ksh之后设计的。不同之处在于:
- 无引号扩展时无括号扩展
- 第一个字符,
$IFS而不是for字节
- 一些特殊情况的区别,例如
$*在非列表上下文中,当$IFS为空时,当不被引用时的扩展。
虽然POSIX规范过去很模糊,但是现在它或多或少指定了bash的行为。
这是从普通数组不同ksh或bash在于:
- 索引从1而不是0开始(除非
"${@:0}"包括$0(不包括位置参数,并且in函数中为您提供函数的名称,取决于外壳程序和函数的定义),否则索引从0开始)。
- 您不能单独分配元素
- 它不是稀疏的,不能单独取消设置元素
shift 可以使用。
在普通数组(zsh或yash稀疏数组,索引不像其他所有shell中的索引一样开始,但ksh / bash除外)中$*被视为普通数组。zsh以$argv作为别名(与兼容csh)。$*与$argv或相同${argv[*]}(参数以的第一个字符开头,$IFS但仍在列表上下文中分开)。"$@"喜欢"${argv[@]}"或"${*[@]}"}经过Korn式的特殊处理。