我们通常用 来$@
表示除 $0 之外的所有参数。但是,我不知道什么$@
是数据结构。
$*
为什么它在包含双引号时表现不同,有人能给我解释器级别的解释吗?
它可以在for循环中迭代,所以它看起来是数组。然而,它也可以完全与 simple 相呼应echo $@
,如果它是一个数组,则只显示第一个元素。由于shell的限制,我无法编写更多的实验代码来进行。
之间的区别这个帖子:这篇文章展示了 的$@
行为与 的不同$*
。但我对 的数据类型感到疑惑$@
。Shell 作为一种解释语言,像 Python 一样,应该根据一系列基本类型来表示数据。或者换句话说,我想知道 $@ 是如何存储在计算机内存中的。
是字符串、多行字符串还是数组?
如果它是唯一的数据类型,是否可以将自定义变量定义为该类型的实例?
答案1
这始于对 Bourne shell 的黑客攻击。在 Bourne shell 中,对列表上下文中的所有单词(命令行参数或for
循环所循环的单词)进行了 IFS 单词分割(在标记化之后)。如果你有:
IFS=i var=file2.txt
edit file.txt $var
第二行将被标记为 3 个单词,$var
将被扩展,并且 split+glob 将在所有三个单词上完成,因此您最终将ed
使用t
, f
, le.txt
, f
,le2.txt
作为参数运行。
引用其中的部分内容可以防止 split+glob。 Bourne shell 最初通过在内部设置第 8 位来记住哪些字符被引用(后来当 Unix 成为 8 位干净时,情况发生了变化,但 shell 仍然做了类似的事情来记住引用了哪个字节)。
和$*
都是$@
位置参数的串联,中间有空格。但$@
双引号内的when有特殊处理。如果$1
包含foo bar
和$2
包含baz
,"$@"
将扩展为:
foo bar baz
^^^^^^^ ^^^
(^
上面的 s 表示哪个字符设置了第 8 位)。其中第一个空格被引用(设置了第 8 位),但没有第二个空格(在单词之间添加的空格)。
IFS 分割负责分隔参数(假设空格字符在$IFS
默认情况下)。这类似于$*
其前身 Mashey shell 的扩展方式(本身基于 Thomson shell,而 Bourne shell 是从头开始编写的)。
这解释了为什么在 Bourne shell 中,"$@"
当位置参数列表为空时(您必须使用 来解决它),Bourne shell 最初会扩展为空字符串而不是任何内容,为什么它不保留空位置参数,为什么不${1+"$@"}
保留空位置参数?不包含空格字符"$@"
时不起作用。$IFS
目的是能够将参数列表逐字传递给另一个命令,但这对于空列表、空元素或$IFS
不包含空格时不能正常工作(前两个问题最终在以后的版本中得到修复) )。
Korn shell(POSIX 规范所基于的)通过几种方式改变了这种行为:
- IFS 拆分仅针对未加引号的扩展的结果进行(而不是针对上面示例中的
edit
或之类的文字单词)file.txt
$*
和当为空时,与 的第一个字符或空格$@
连接,但对于带引号的,该连接符像 Bourne shell 中一样不带引号,而对于带引号的,当为空时,附加位置参数,不带分隔符。$IFS
$IFS
"$@"
"$*"
IFS
- 它增加了对数组的支持,
${array[@]}
${array[*]}
让人想起 Bourne 的$*
and$@
但从索引 0 而不是 1 开始,并且稀疏(更像关联数组),这意味着$@
不能真正被视为 ksh 数组(与csh
/rc
/zsh
/fish
/相比,yash
其中$argv
/$*
是正常的数组)。 - 空元素被保留。
"$@"
当$#
为 0 时,现在将扩展为空字符串,而不是空字符串,当不包含空格"$@"
时有效,除非为空。当为空时,不带通配符的未加引号的字符串将扩展为一个参数(其中位置参数用空格连接)。$IFS
IFS
$*
$IFS
ksh93 修复了上面剩下的几个问题。在 ksh93 中,$*
and$@
扩展为位置参数列表,无论 的值如何都分开$IFS
,然后在列表上下文中进一步 split+globbed+brace-expanded,$*
与first 连接字节(不是字符) of $IFS
,"$@"
在列表上下文中扩展为位置参数列表,无论 的值如何$IFS
。在非列表上下文中,例如 in var=$@
,$@
无论 的值如何,都会与空格连接$IFS
。
bash
的数组是在 ksh 数组之后设计的。差异是:
- 无引号扩展时不进行大括号扩展
- 的第一个字符
$IFS
代替 for 字节 $*
一些极端情况的差异,例如当为空时在非列表上下文中未引用时的扩展$IFS
。
虽然 POSIX 规范过去相当模糊,但现在它或多或少指定了 bash 行为。
它与普通数组的不同之处ksh
在于bash
:
- 索引从 1 而不是 0 开始(除了 in
"${@:0}"
其中 include$0
(不是位置参数,并且在函数中给出函数名称或不给出函数名称,具体取决于 shell 以及函数的定义方式))。 - 您不能单独分配元素
- 它不是稀疏的,你不能单独取消设置元素
shift
可以使用。
在数组为普通数组(不稀疏,索引从 1 开始,除了 ksh/bash 之外的所有其他 shell 中)时,zsh
被视为普通数组。有作为它的别名(为了与 兼容)。与or相同(参数与 of 的第一个字符连接,但在列表上下文中仍然分开)。类似或经过科恩式特殊加工。yash
$*
zsh
$argv
csh
$*
$argv
${argv[*]}
$IFS
"$@"
"${argv[@]}"
"${*[@]}"}
答案2
但是,我不知道什么
$@
是数据结构。
这是一个特殊的参数,可以扩展为位置参数的值……但这对术语来说是挑剔的。
我们可以将位置参数视为 的一部分$@
,因此它具有许多不同的元素($1
, $2
...),这些元素可以独立访问并由连续的自然数命名。这使得它通常被称为数组。
不过,语法有点奇怪,甚至是有限的。无法单独修改数组的单个元素。相反,整个事情必须立即设置。 (您可以使用set -- "$@" foo
附加值,或set -- "${@:1:2}" foo "${@:3}"
在中间添加值。但是在这两种情况下,您都必须写出整个结果列表。)
为什么它的行为与
$*
包含双引号时不同,
因为它们的行为被定义为不同的。
然而,它也可以完全与 simple 相呼应
echo $@
,如果它是一个数组,则只显示第一个元素。
如果您的意思是a=(foo bar asdf); echo $a
仅输出foo
,那么这主要是 shell 语法的一个怪癖,并且 ksh 样式命名数组的创建晚于位置参数 和$@
。 Plain$a
是相同的,${a[0]}
因此它具有单个标量值的向后兼容含义,无论a
是数组还是简单标量变量。
@
引用整个列表的符号与命名数组一起重复使用,这是"${a[@]}"
获取整个列表的方法。与命名数组相比, with $@
,不必要的大括号和方括号以及名称都被跳过了。
或者换句话说,我想知道如何
$@
存储在计算机内存中。
这取决于实现,您必须查看您关心的任何特定 shell 的源代码。
是字符串、多行字符串还是数组?
主要是一个数组。尽管与 ksh 风格的命名数组不同,但它们可以具有任意非负整数作为索引,而不仅仅是像 那样的连续整数$@
。 (也就是说,命名数组可以是稀疏的,并且具有例如索引1
、3
、4
、 with0
和2
missing 。这对于位置参数来说是不可能的。)
它不是单个字符串,因为它可以扩展到不同的元素,并且调用元素行也是不正确的,因为任何常规变量或位置参数之一( 的元素$@
)也可以包含换行符。
如果它是唯一的数据类型,是否可以将自定义变量定义为该类型的实例?
不,但是命名数组可能更有用。