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

dav*_*mos 13 shell bash

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

为什么$*在包含双引号时它的行为不同,谁能给我一个解释级别的解释?

可以在for循环中迭代,所以好像是数组。但是,它也可以完全用 simple 回显echo $@,如果是数组,则只显示第一个元素。由于shell的限制,我无法编写更多的实验代码来执行它。

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

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

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

Sté*_*las 16

这开始是对 Bourne shell 的一次黑客攻击。在 Bourne shell 中,对列表上下文中的所有单词(命令行参数或for循环循环的单词)进行了 IFS 分词(在标记化之后)。如果你有:

IFS=i var=file2.txt
edit file.txt $var
Run Code Online (Sandbox Code Playgroud)

第二行将被标记为 3 个单词,$var将被扩展,并且 split+glob 将在所有三个单词上完成,因此您最终将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
^^^^^^^ ^^^
Run Code Online (Sandbox Code Playgroud)

^上面的s 表示哪些字符设置了第 8 位)。其中第一个空格被引用(设置了第 8 位)而不是第二个空格(在单词之间添加的那个)。

并且 IFS 拆分负责分隔参数(假设$IFS默认情况下空格字符在其中)。这类似于$*在其前身 Mashey shell 中的扩展方式(它本身基于 Thomson shell,而 Bourne shell 是从头开始编写的)。

这就解释了为什么在Bourne shell中最初"$@"将扩展为空字符串,而不是什么都没有的时候位置参数列表是空的(你不得不工作,它与周围的${1+"$@"}),为什么没有保持空位置参数,为什么"$@"没'$IFS不包含空格字符时不起作用。

目的是能够将参数列表逐字传递给另一个命令,但这对于空列表、空元素或$IFS不包含空格时无法正常工作(前两个问题最终在以后的版本中得到修复)。

Korn shell(POSIX 规范所基于的)以几种方式改变了这种行为:

  • IFS 拆分仅对不带引号的扩展的结果进行(而不是对像editfile.txt在上面的示例中的字面词)
  • $*and当为空时$@$IFS或 空格的第一个字符连接,$IFS除了带引号的"$@",该连接器像在 Bourne shell 中一样不带引号,并且对于带引号的"$*"whenIFS为空,位置参数被附加而没有分隔符。
  • 它增加了对数组的支持,并且${array[@]} ${array[*]}让人想起了 Bourne's $*and$@但从索引 0 而不是 1 开始,并且稀疏(更像是关联数组)这意味着$@不能真正被视为 ksh 数组(与csh/ rc/ zsh/ fish/相比,yash其中$argv/$*是正常的数组)。
  • 保留空元素。
  • "$@"when $#is 0 现在扩展为空字符串而不是空字符串,"$@"$IFS不包含空格时有效,除非 whenIFS为空。$*$IFS为空时,不带通配符的未加引号扩展为一个参数(其中位置参数与空格连接)。

ksh93 修复了上面剩下的几个问题。在 ksh93 中,$*$@扩展为位置参数列表,无论 的值如何都分开$IFS,然后在列表上下文中进一步 split+globbed+brace-expanded,$*与 的第一个字节(不是字符)连接$IFS"$@"在列表上下文中扩展为列表的位置参数,而不管 的值$IFS。在非列表上下文中,如 in var=$@$@无论 的值如何,都与空格连接$IFS

bash的数组是在 ksh 之后设计的。区别在于:

  • 无引号扩展时没有大括号扩展
  • $IFS代替字节的第一个字符
  • 一些特殊情况的差异,例如$*$IFS为空时在非列表上下文中未引用时的扩展。

虽然 POSIX 规范过去非常模糊,但现在或多或少指定了 bash 行为。

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

  • 索引从 1 而不是 0 开始(除了 in"${@:0}"包含$0(不是位置参数,并且在函数中根据 shell 和函数的定义方式为您提供函数的名称))。
  • 您不能单独分配元素
  • 它不是稀疏的,您不能单独取消设置元素
  • shift 可以使用。

zshyash其中阵列是正常阵列(未稀疏,索引从1开始像在所有其他弹但KSH / bash)的,$*被视为普通阵列。zsh$argv作为它的别名(为了与 兼容csh)。$*$argvor相同${argv[*]}(参数与 of 的第一个字符连接$IFS但在列表上下文中仍分开)。"$@"喜欢"${argv[@]}""${*[@]}"}经过 Korn 式特殊处理。


ilk*_*chu 8

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

这是一个特殊的参数,可以扩展到位置参数的值......但这是对术语的挑剔。

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

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

为什么$*在包含双引号时它的行为不同,

因为他们被定义为行为不同。

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

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

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

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

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

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

一个数组,主要是。尽管与 ksh 样式的命名数组不同,因为它们可以将任意非负整数作为索引,而不仅仅是像$@. (也就是说,命名数组可以是稀疏的,并且具有例如索引1, 3and 4, with02missing 。这对于位置参数是不可能的。)

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

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

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