bash是如何以及为什么分裂的;{xs[@]}";争论?
我对bash如何将一行分割成程序的参数有基本的了解,足以避免包含空格的参数出现问题,但我想多做一步,了解发生了什么以及原因。大多数指南会告诉你该做什么,但不会告诉你为什么它会起作用。一些例子可能有助于解释 我将使用这个简短的Python脚本转储参数列表:bash是如何以及为什么分裂的;{xs[@]}";争论?,bash,Bash,我对bash如何将一行分割成程序的参数有基本的了解,足以避免包含空格的参数出现问题,但我想多做一步,了解发生了什么以及原因。大多数指南会告诉你该做什么,但不会告诉你为什么它会起作用。一些例子可能有助于解释 我将使用这个简短的Python脚本转储参数列表: #!/usr/bin/env python import sys print sys.argv[1:] 让我们称之为“dumpargs”。(您可以用C甚至bash编写,但Python足够简洁,我不想用额外一层bash解释和扩展字符串来混淆问题
#!/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引入数组时,它们保持对称性(如果没有$*
,就无法将数组转换为单个字符串)
相关的:
set-x
和echo
代替dumpargs
会解释很多。也就是说,我在这里看到了两个问题:(1)@
和*
之间的区别,(2)引用变量的影响;这两件事都问了很多遍。“真的,我不需要知道这个来解决眼前的问题,但我认为这是一个例子:“给某人一条鱼,他们会吃一天,教他们钓鱼,他们会吃一辈子。”我想要更深入的了解,但我发现很难找到比“给我一条鱼”更有用的向导。这是有记录的行为:@glennjackman谢谢!“数组的任何元素…”开头的一段确实解释得很清楚。我只是没找对地方。我觉得自己没有我希望的那么聪明,但这恰恰回答了我提出的问题,而且是从最权威的来源得到的。如果作为答案发布,我会接受。@devnull:想解释一下你的评论吗?@Aarondigula如果OP真的做了这么多研究,说set-x
和echo
而不是dumpargs
会解释很多。也就是说,我在这里看到了两个问题:(1)@
和*
之间的区别,(2)引用变量的影响;这两件事都问了很多遍。“真的,我不需要知道这件事来解决眼前的问题,但我认为这是一个例子:”给某人一条鱼,他们会吃一天,教他们。