【发布时间】:2014-03-27 13:42:09
【问题描述】:
我对 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 如何以及何时将数组呈现为字符序列,以及它如何以及何时拆分参数?
【问题讨论】:
-
@devnull:想解释一下你的评论吗?
-
@AaronDigulla 如果 OP 确实做了很多研究,说
set -x和echo而不是dumpargs会解释很多。也就是说,我在这里看到了 2 个问题:(1)@和*之间的区别,(2)引用变量的影响;这两个都问过很多次了。 -
@devnull 不。确实,我并不需要知道这一点来解决眼前的问题,但我认为这是“给某人一条鱼,他们“吃一天,教他们钓鱼,吃一辈子”。我想要更深入的了解,但我发现很难找到除了“给我一条鱼”以外的其他功能。
-
@glennjackman 谢谢!以“数组的任何元素......”开头的段落确实准确地解释了它。我只是没有找对地方。我感觉没有我希望的那么聪明,但这正是我提出的问题的答案,并且来自最权威的来源。如果作为答案发布,我会接受。
标签: bash