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

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

我们通常用 来$@表示除 $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 时,现在将扩展为空字符串,而不是空字符串,当不包含空格"$@"时有效,除非为空。当为空时,不带通配符的未加引号的字符串将扩展为一个参数(其中位置参数用空格连接)。$IFSIFS$*$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$argvcsh$*$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 风格的命名数组不同,但它们可以具有任意非负整数作为索引,而不仅仅是像 那样的连续整数$@。 (也就是说,命名数组可以是稀疏的,并且具有例如索引134、 with02missing 。这对于位置参数来说是不可能的。)

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

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

不,但是命名数组可能更有用。

相关内容