据我了解,星号字符*
在 Bash 中具有特殊含义,为了打印文字字符,我们需要转义它 ( \*
) 或引用它 ("*"
或'*'
.
但是,当我尝试过以下操作时:
$ seq 3 | xargs -I * echo *
1
2
3
*
为什么 Bash在上面的例子中不解释?但在下面的示例中,它确实将其解释为通配符(它与当前目录中的文件匹配):
$ seq 3 | xargs -I* echo * # no space between -I and *
main.py
main.py
main.py
您能解释一下这背后的逻辑吗?
谢谢
答案1
重击做过展开第一个示例中的星号。
假设当前目录仅包含一个文件 ,main.py
则发生的情况如下:
$ seq 3 | xargs -I * echo *
# Expands to:
$ seq 3 | xargs -I main.py echo main.py
echo main.py
这要求对每个输入执行操作,并替换main.py
为该输入(因为-I main.py
)。这就是您所观察到的:三个模板从echo main.py
变为echo 1
, echo 2
,
echo 3
。
在第二种情况下,星号就在旁边,以-I
防止该单词扩展(因为没有文件与模式匹配-I*
):
$ seq 3 | xargs -I* echo *
# Expands to:
$ seq 3 | xargs -I* echo main.py
这要求 xargs 用输入替换*
模板中的(字面星号)的实例。echo main.py
由于模板*
根本不包含占位符,因此main.py
每次都会打印。
(failglob
在 Bash 中启用或在 zsh 中使用默认设置时,不匹配任何内容的 glob 将产生错误,并且该命令将不会运行。)
如果您的命令保护了两次出现的*
,那么您将获得与第一次尝试相同的输出:
$ seq 3 | xargs -I '*' echo '*'
1
2
3
如果目录中碰巧有多个文件,那么未加引号的文件*
可能会产生更明显的问题。例如,如果有两个文件main.py
和other.py
,则:
$ seq 3 | xargs -I * echo *
# Expands to:
$ seq 3 | xargs -I main.py other.py echo main.py other.py
并将尝试作为命令xargs
运行(由当前输入字符串替换)。这可能会给出“命令未找到”错误。或者,如果排序顺序中的第二个文件名与 中的可运行程序匹配,则将运行other.py echo main.py other.py
main.py
xargs
PATH
xargs
那个程序, 并不是echo
。
获得 Bash 扩展发生情况的一定程度可见性的一个有用技巧是运行set -o xtrace
。设置标志后,您可以看到以 开头的行的扩展+
:
$ set -o xtrace
$ seq 3 | xargs -I * echo *
+ seq 3
+ xargs -I main.py echo main.py
1
2
3
$ seq 3 | xargs -I* echo *
+ seq 3
+ xargs '-I*' echo main.py
main.py
main.py
main.py
$ seq 3 | xargs -I '*' echo '*'
+ seq 3
+ xargs -I '*' echo '*'
1
2
3