bash是如何以及为什么分裂的;{xs[@]}";争论?

bash是如何以及为什么分裂的;{xs[@]}";争论?,bash,Bash,我对bash如何将一行分割成程序的参数有基本的了解,足以避免包含空格的参数出现问题,但我想多做一步,了解发生了什么以及原因。大多数指南会告诉你该做什么,但不会告诉你为什么它会起作用。一些例子可能有助于解释 我将使用这个简短的Python脚本转储参数列表: #!/usr/bin/env python import sys print sys.argv[1:] 让我们称之为“dumpargs”。(您可以用C甚至bash编写,但Python足够简洁,我不想用额外一层bash解释和扩展字符串来混淆问题

我对bash如何将一行分割成程序的参数有基本的了解,足以避免包含空格的参数出现问题,但我想多做一步,了解发生了什么以及原因。大多数指南会告诉你该做什么,但不会告诉你为什么它会起作用。一些例子可能有助于解释

我将使用这个简短的Python脚本转储参数列表:

#!/usr/bin/env python
import sys
print sys.argv[1:]
让我们称之为“dumpargs”。(您可以用C甚至bash编写,但Python足够简洁,我不想用额外一层bash解释和扩展字符串来混淆问题。)

首先,一些简单的例子:

$ dumpargs foo bar baz
['foo', 'bar', 'baz']
$ dumpargs "foo bar" baz
['foo bar', 'baz']
好的,很好。我们可以使用引号来传递包含空格的参数,方法是将引号括起来。但我们并不局限于把引语放在论点的外面。如果我们把它们放在中间怎么办?

$ dumpargs foo" "bar
['foo bar']
$ dumpargs foo" "bar" "baz xyzzy
['foo bar baz', 'xyzzy']
好的,酷。我认为这表明引号只是修改了空格的解释方式。双引号之间的空格不是参数分隔符。不带引号的空格变成分隔符,带引号的空格变成真正的空格,引号消失

阵列呢

$ xs=(one two "buckle my shoe")

$ dumpargs ${xs[*]}
['one', 'two', 'buckle', 'my', 'shoe']
$ dumpargs ${xs[@]}
['one', 'two', 'buckle', 'my', 'shoe']
$ dumpargs "${xs[*]}"
['one two buckle my shoe']
$ dumpargs "${xs[@]}"
['one', 'two', 'buckle my shoe']
显然,这四个数组中的最后一个通常最有用,并且很可能是我们希望在数组表示(比如)文件名列表的地方使用的。其他的都把
“buckle my shoe”
中的空格与数组元素之间的分隔符混淆了。但它到底在做什么?它看起来像是由变量展开和引用操作组成的。它是?或者bash只是对在数组扩展周围立即出现双引号的情况进行了特殊处理

下面是一些更多的例子来测试正在发生的事情:

$ xs=(one two "buckle my shoe")

$ dumpargs "${xs[@]} stop"
['one', 'two', 'buckle my shoe stop']
$ dumpargs "${xs[@]} and ${xs[@]}"
['one', 'two', 'buckle my shoe and one', 'two', 'buckle my shoe']
我认为这至少表明,它不仅仅是直接围绕数组扩展的一对引号的特殊情况。数组扩展产生某种类似字符串的输出,引号影响类似字符串的内容如何转换为一系列参数。但它不仅仅是一个简单的字符串,因为它有两种不同的类似于空格的东西。它有一些“参数分隔符”,不管引号如何都会成为参数分隔符,但它也有“诚实到善良的空间”,如果它们被引号包围,就不会成为参数分隔符。相反,
${xs[*]}
输出一个只有“诚实到善良的空格”且没有特殊“参数分隔符”的常规字符串


这是理解它的好方法吗?有没有更好的方法来理解bash如何以及何时将数组呈现为字符序列,以及它如何以及何时拆分参数?

这种行为的根源可能是旧的“将参数传递给子shell”问题。起初,我们有
$*
,它一直工作到您开始在参数中使用空格为止

 Input         Subshell sees
 a b           "a" "b"
 "a b"         "a" "b"
 a b\ c        "a" "b" "c" 
 a b\\\ c      "a" "b c" 
我们可以引用
$*
,但这会将所有参数合并到一个字符串参数中(即,子shell将始终看到
“ab”
“abc”
)。显然,这是不好的

因此引入了
@
表单。如果没有引号,
$*
$@
的行为类似。使用引号-
“$@”
-展开为正确引用的参数列表

 Input         Subshell sees
 a b           "a" "b"
 "a b"         "a" "b"
 a b\ c        "a" "b" "c" 
 a b\\\ c      "a" "b c" 
当KSH/BASH引入数组时,它们保持对称性(如果没有
$*
,就无法将数组转换为单个字符串)

相关的:


    • 这种行为的根源可能是旧的“将参数传递给子shell”问题。起初,我们有
      $*
      ,它一直工作到您开始在参数中使用空格为止

       Input         Subshell sees
       a b           "a" "b"
       "a b"         "a" "b"
       a b\ c        "a" "b" "c" 
       a b\\\ c      "a" "b c" 
      
      我们可以引用
      $*
      ,但这会将所有参数合并到一个字符串参数中(即,子shell将始终看到
      “ab”
      “abc”
      )。显然,这是不好的

      因此引入了
      @
      表单。如果没有引号,
      $*
      $@
      的行为类似。使用引号-
      “$@”
      -展开为正确引用的参数列表

       Input         Subshell sees
       a b           "a" "b"
       "a b"         "a" "b"
       a b\ c        "a" "b" "c" 
       a b\\\ c      "a" "b c" 
      
      当KSH/BASH引入数组时,它们保持对称性(如果没有
      $*
      ,就无法将数组转换为单个字符串)

      相关的:


      @devnull:想解释一下你的评论吗?@Aarondigula如果OP真的做了这么多研究,用
      set-x
      echo
      代替
      dumpargs
      会解释很多。也就是说,我在这里看到了两个问题:(1)
      @
      *
      之间的区别,(2)引用变量的影响;这两件事都问了很多遍。“真的,我不需要知道这个来解决眼前的问题,但我认为这是一个例子:“给某人一条鱼,他们会吃一天,教他们钓鱼,他们会吃一辈子。”我想要更深入的了解,但我发现很难找到比“给我一条鱼”更有用的向导。这是有记录的行为:@glennjackman谢谢!“数组的任何元素…”开头的一段确实解释得很清楚。我只是没找对地方。我觉得自己没有我希望的那么聪明,但这恰恰回答了我提出的问题,而且是从最权威的来源得到的。如果作为答案发布,我会接受。@devnull:想解释一下你的评论吗?@Aarondigula如果OP真的做了这么多研究,说
      set-x
      echo
      而不是
      dumpargs
      会解释很多。也就是说,我在这里看到了两个问题:(1)
      @
      *
      之间的区别,(2)引用变量的影响;这两件事都问了很多遍。“真的,我不需要知道这件事来解决眼前的问题,但我认为这是一个例子:”给某人一条鱼,他们会吃一天,教他们。