我对 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 Shoes”中的空格与数组元素之间的分隔符混淆了。但它实际上在做什么?看起来它是由变量扩展和引用操作组成的。是吗?或者当 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 总是会看到 "a b"
或 "a b c"
)。显然,这不好。
因此引入了@
形式。如果没有引号,$*
和 $@
的行为类似。带引号 - "$@"
- 扩展为正确引用的参数列表。
当 KSH/BASH 引入数组时,它们保持对称性(没有 $*
,你无法将数组变成单个字符串)。
相关:
关于bash - bash 如何以及为何将 "{xs[@]}"拆分为参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22012416/