14

我有一些看起来像的文件:

/path/with spaces/{a,b,c}/*.gz

而且我需要在a,b,c目录子集下与 glob 匹配的所有文件最终作为单个命令的参数:

mycmd '/path/with spaces/a/1.gz' '/path/with spaces/a/2.gz' '/path/with spaces/c/3.gz' ...

我关心的目录以命令行参数的形式出现,我将它们放在一个数组中:

dirs=( "$@" )

我想做类似的事情:

IFS=,
mycmd "/path/with spaces/{${dirs[*]}}/"*.gz

但这不起作用,因为 bash 在变量之前扩展了大括号。我尝试过使用 and 甚至 (*shudder*) 的技巧,echols很难eval让它们与文件名中的空格一起使用。 find似乎没有太大帮助,因为它不做大括号。我可以为数组中的每个目录获取一个单独的 glob:

dirs=( "${dirs[@]/#//path/with spaces/}" )
dirs=( "${dirs[@]/%//*.gz}" )

但随后 bash 在扩展时引用通配符。

那么:有没有一种优雅的方法来获取与变量大括号和 glob 模式匹配的所有文件,正确处理空格,还是我坚持做 for 循环?如果这有所作为,我正在使用 Bash 3。

4

3 回答 3

12

要在带有空格的路径上执行大括号扩展和通配符,您可以引用路径中包含空格的部分,例如

mycmd '/path/with spaces/'{a,b,c}/*.gz

使用来自变量的值列表进行大括号扩展有点棘手,因为大括号扩展是在任何其他扩展之前完成的。除了使用 dreaded 之外,我没有其他办法eval

eval mycmd "'/path/with spaces/'{a,b,c}/*.gz"

PS 然而,在这种情况下,我个人会选择循环来构建参数列表,而不是上面显示的方法。虽然更冗长,但对于初学者来说,循环会更容易阅读,并且可以避免使用eval(尤其是当扩展候选者来自用户输入时!)。


概念证明:

使用打印出参数数量并打印出每个参数的虚拟命令 (x.sh):

[me@home]$ shopt -s nullglob # 处理 globbing 返回不匹配的情况

[me@home]$ ./x.sh '带空格的路径'/{a,b}/*.txt
参数数量 = 3
- 带空格的路径/a/1.txt
- 带空格的路径/b/2.txt
- 带空格的路径/b/3.txt

[me@home]:~/temp$ dirs="a,b"
[me@home]k:~/temp$ eval ./x.sh "'带空格的路径'/{$dirs}/*.txt"
参数数量 = 3
- 带空格的路径/a/1.txt
- 带空格的路径/b/2.txt
- 带空格的路径/b/3.txt
于 2013-01-17T15:20:39.240 回答
4

好的,这是一个使用 bash 作为“大括号”和findglob 的方法:

find "${dirs[@]/#//path/with spaces/}" -name '*.gz' -print0 | xargs -0 mycmd

如果您需要数组中的结果,很有用。

于 2013-01-17T15:21:04.150 回答
2

这是GNU Parallel粉丝的一个:

parallel -Xj1 mycmd {}/*.gz ::: "${dirs[@]/#//path/with spaces/}"
于 2013-01-17T16:11:44.030 回答