使用生成的文件名列表作为参数列表——带空格

ale*_*xis 21 shell bash find quoting command-substitution

我正在尝试使用由find. 没什么特别的,就是这样:

$ myscript `find . -name something.txt`
Run Code Online (Sandbox Code Playgroud)

问题是一些路径名包含空格,因此它们在参数扩展时被分解为两个无效名称。通常我会用引号将名称括起来,但在这里它们是由反引号扩展插入的。我试过find用引号过滤每个文件名的输出和周围,但是当 bash 看到它们时,剥离它们为时已晚,它们被视为文件名的一部分:

$ myscript `find . -name something.txt | sed 's/.*/"&"/'`
No such file or directory: '"./somedir/something.txt"'
Run Code Online (Sandbox Code Playgroud)

是的,这是如何处理命令行的规则,但我该如何解决呢?

这很尴尬,但我没有想出正确的方法。我终于想出了如何使用xargs -0 -n 10000...但它是如此丑陋的黑客,我仍然想问:我如何引用反引号扩展的结果,或者以另一种方式实现相同的效果?

编辑:我感到困惑的是,事实xargs 确实收集所有参数到一个单一的参数列表,除非它告诉别人,否则或系统限制可能被超过。感谢大家让我直截了当!其他人,在阅读已接受的答案时请记住这一点,因为它没有直接指出。

我已经接受了答案,但我的问题仍然存在:是否有某种方法可以保护反引号(或$(...))扩展中的空格?(请注意,接受的解决方案是非 bash 答案)。

slm*_*slm 16

你可以这样做使用一些实现以下findxargs这样。

$ find . -type f -print0 | xargs -r0 ./myscript
Run Code Online (Sandbox Code Playgroud)

或者,通常,只是find

$ find . -type f -exec ./myscript {} +
Run Code Online (Sandbox Code Playgroud)

例子

假设我有以下示例目录。

$ tree
.
|-- dir1
|   `-- a\ file1.txt
|-- dir2
|   `-- a\ file2.txt
|-- dir3
|   `-- a\ file3.txt
`-- myscript

3 directories, 4 files
Run Code Online (Sandbox Code Playgroud)

现在让我们说我有这个./myscript

#!/bin/bash

for i in "$@"; do
    echo "file: $i"
done
Run Code Online (Sandbox Code Playgroud)

现在,当我运行以下命令时。

$ find . -type f -print0 | xargs -r0 ./myscript 
file: ./dir2/a file2.txt
file: ./dir3/a file3.txt
file: ./dir1/a file1.txt
file: ./myscript
Run Code Online (Sandbox Code Playgroud)

或者当我像这样使用第二种形式时:

$ find . -type f -exec ./myscript {} +
file: ./dir2/a file2.txt
file: ./dir3/a file3.txt
file: ./dir1/a file1.txt
file: ./myscript
Run Code Online (Sandbox Code Playgroud)

细节

查找 + xargs

以上两种方法虽然看起来不同,但本质上是相同的。第一个是获取 find 的输出,\0通过-print0switch使用 NULL ( )将其拆分以进行查找。该xargs -0专门采取输入这是使用NULL的分裂。这种非标准语法是由 GNU 引入的findxargs但现在也出现在其他一些像最近的 BSD 中。如果在 GNU 中没有发现任何内容,但在 BSD 中没有发现,-r则需要该选项来避免调用。myscriptfindfind

注意:整个方法取决于这样一个事实,即您永远不会传递过长的字符串。如果是,则第二次调用./myscript将被 find 的其余后续结果启动。

用 + 查找

这是标准方式(尽管它最近才被添加到 GNU 实现中(2005 年)find)。做我们正在做的事情的能力xargs实际上是内置在find. Sofind将找到一个文件列表,然后将尽可能多的参数传递给该列表以适应之后指定的命令-exec(注意,在这种情况下{}只能是最后一个+),如果需要,多次运行命令。

为什么不引用?

在第一个示例中,我们通过使用 NULL 分隔参数来完全避免引用问题,从而走捷径。当xargs给出这个列表时,它被指示在 NULL 上拆分,有效地保护我们的单个命令原子。

在第二个示例中,我们将结果保存在内部find,因此它知道每个文件原子是什么,并保证适当地处理它们,从而避免引用它们的麻烦事。

命令行的最大大小?

这个问题不时出现,所以作为奖励,我将它添加到这个答案中,主要是为了我将来可以找到它。您可以使用以下xargs方式查看环境的限制:

$ xargs --show-limits
Your environment variables take up 4791 bytes
POSIX upper limit on argument length (this system): 2090313
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2085522
Size of command buffer we are actually using: 131072
Run Code Online (Sandbox Code Playgroud)