我基本了解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如何以及何时将数组呈现为一个字符序列以及它何时何地分裂参数?
答案 0 :(得分:4)
此行为的起源可能是旧的“传递SUBhell参数”问题。在开始时,我们有$*
,直到你开始在参数中使用空格。
Input Subshell sees
a b "a" "b"
"a b" "a" "b"
a b\ c "a" "b" "c"
a b\\\ c "a" "b c"
我们可以引用$*
但是这会将所有参数合并为单个字符串参数(即子shell总是会看到"a b"
或"a b c"
)。显然,这不好。
因此引入了@
表单。如果没有引号,$*
和$@
的行为相似。引号 - "$@"
- 扩展为正确引用的参数列表。
当KSH / BASH引入数组时,它们保持对称(没有$*
,你无法将数组转换成单个字符串)。
相关: