shell中$ @的数据结构是什么?


13

我们通常$@用来表示除$ 0以外的所有参数。但是,我不知道数据结构$@是什么。

为什么$*在包含双引号时它的行为方式不同,有人可以给我解释级别的解释吗?

可以在for循环中对其进行迭代,因此它似乎是数组。但是,它也可以使用simple完全回显echo $@,如果它是一个数组,则仅显示第一个元素。由于外壳的限制,我无法编写更多的实验代码来执行它。

这篇文章之间的区别:这篇文章展示了$@$*。但是我想知道的数据类型$@。像Python一样,Shell是一种解释语言,应根据一系列基本类型表示数据。换句话说,我想知道$ @如何存储在计算机内存中。

它是字符串,多行字符串还是数组?

如果它是唯一数据类型,是否可以将自定义变量定义为这种类型的实例?



@Haxiel,我不这么认为,我在帖子底部写下了它们的区别。
davmos

使用printf '%s\n' "$@"和测试输出的差异会更好printf '%s\n' "$*"。该echo实用程序仅输出其参数,而不管它们是一个还是多个。两者都是(字符串的)数组,但是在用双引号引起来时它们的行为有所不同。如果其中一个是多行字符串,则它们将无法存储多行字符串(可以)。目前尚不清楚您要解决什么问题。
库沙兰丹

2
您的问题等同于询问@varPerl中的变量及其底层存储。从一个普通的Perl程序的角度来看,它并不重要,除了它可以作为数组/列表(以及存在期望列表的上下文)访问之外。
库萨兰达

Answers:


16

这始于Bourne外壳的破解。在Bourne Shell中,对列表上下文中的所有单词(命令行参数或for循环循环的单词)进行了IFS单词拆分(在标记化之后)。如果你有:

IFS=i var=file2.txt
edit file.txt $var

这第二条线将在3个字被标记化,$var将扩大和拆分+水珠会在所有三个词来完成的,所以你最终会运行edtfle.txtfle2.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拆分仅针对未加引号的扩展结果(不适用于上面的示例中的editfile.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的行为。

这是从普通数组不同kshbash在于:

  • 索引从1而不是0开始(除非"${@:0}"包括$0(不包括位置参数,并且in函数中为您提供函数的名称,取决于外壳程序和函数的定义),否则索引从0开始)。
  • 您不能单独分配元素
  • 它不是稀疏的,不能单独取消设置元素
  • shift 可以使用。

在普通数组(zshyash稀疏数组,索引不像其他所有shell中的索引一样开始,但ksh / bash除外)中$*被视为普通数组。zsh$argv作为别名(与兼容csh)。$*$argv或相同${argv[*]}(参数以的第一个​​字符开头,$IFS但仍在列表上下文中分开)。"$@"喜欢"${argv[@]}""${*[@]}"}经过Korn式的特殊处理。


8

但是,我不知道数据结构$@是什么。

这是一个特殊的参数,可扩展为位置参数的值...但是,这与术语无关。

我们可以将位置参数视为的一部分$@,因此它具有许多不同的元素($1$2...),这些元素可以独立访问,并由连续的自然数命名。这使得它通常被称为数组。

语法有点怪异,甚至是有限的。无法单独修改数组的单个元素。相反,必须立即设置整个过程。(您可以用来set -- "$@" foo附加值,也set -- "${@:1:2}" foo "${@:3}"可以在中间添加值。但是在两种情况下,您都必须写出整个结果列表。)

为什么$*在包含双引号的情况下其行为会有所不同,

因为它们被定义为具有不同的行为。

但是,它也可以使用simple完全回显echo $@,如果它是一个数组,则仅显示第一个元素。

如果您要说的是a=(foo bar asdf); echo $a将输出公正的事实foo,那么这主要是shell语法的怪癖,以及ksh样式的命名数组的创建晚于位置参数and的事实$@。Plain 与之$a相同,${a[0]}因此它具有单个标量值的向后兼容含义,无论a是数组还是简单的标量变量。

@引用整个列表的符号已与命名数组一起重用,这"${a[@]}"是获取整个列表的方式。与命名数组相比,使用$@可以跳过不必要的花括号和方括号以及名称。

换句话说,我想知道如何$@存储在计算机内存中。

这取决于实现,您必须查看您关心的任何特定Shell的源代码。

它是字符串,多行字符串还是数组?

一个数组,主要是。尽管与ksh样式的命名数组不同,但由于它们可以具有任意的非负整数作为索引,而不仅仅是与一样的连续整数$@。(也就是说,一个命名数组可以是稀疏的,并且具有例如indexs 134,缺少02。这对于位置参数是不可能的。)

它不是单个字符串,因为它可以扩展为不同的元素,并且调用元素的行也不正确,因为任何常规变量或位置参数之一(的元素$@)也可以包含换行符。

如果它是唯一数据类型,是否可以将自定义变量定义为这种类型的实例?

不会。但是无论如何,命名数组可能更有用。


1
+1。TL:DR $@不是数据结构,它是用于扩展位置参数数据结构的少数函数/运算符之一。
彼得·科德斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.