Bash:如何在文件名中包含空格时扩展大括号和通配符?

17

我有一些类似于以下格式的文件:

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

我需要将匹配全局模式的所有文件仅限于 a,b,c 目录子集,作为单个命令的参数:

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在变量之前扩展花括号。我尝试了使用echo、ls和甚至eval(*发抖*)的技巧,但是很难让它们适用于文件名中带有空格的情况。find似乎没什么帮助,因为它不执行花括号。我可以使用以下方式将每个目录的单独glob存储到数组中:

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

但是在扩展时,bash会引用通配符。

那么,是否有一种优雅的方法来获取匹配变量大括号和glob模式的所有文件,正确处理空格,或者我只能使用for循环?如果这有区别,我正在使用Bash 3。

3个回答

15

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

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

如果要使用变量中的值进行大括号展开,会有一些麻烦,因为大括号展开是在任何其他展开之前完成的。我认为唯一的方式就是使用可怕的 eval

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

顺带一提,若出现这种情况,我个人会选择使用循环来构建参数列表而不是上面展示的方法。虽然更冗长,但循环对于不熟悉代码的人来说更易读,并且避免了使用 eval(尤其是有用户输入的扩展候选时!)。


概念证明:

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

[me@home]$ shopt -s nullglob  # 处理无匹配情况
[me@home]$ ./x.sh 'path with space'/{a,b}/*.txt 参数数量 = 3 - path with space/a/1.txt - path with space/b/2.txt - path with space/b/3.txt
[me@home]:~/temp$ dirs="a,b" [me@home]k:~/temp$ eval ./x.sh "'path with space'/{$dirs}/*.txt" 参数数量 = 3 - path with space/a/1.txt - path with space/b/2.txt - path with space/b/3.txt

5

好的,下面是使用bash进行"括号"匹配和find进行通配符匹配的示例:

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

如果您需要将结果存储在数组中,则使用此方法很有用。


“xargs” 不是会为每个参数运行一次命令,而不是一次运行多个参数吗? - Shawn Chin
1
xargs 可以同时执行两种操作,但默认情况下会尽可能多地将参数传递给一个命令。你可以自己试一下:seq 10 | xargs echo - Jay Hacker

2
这是给GNU Parallel的粉丝们的一个例子:
parallel -Xj1 mycmd {}/*.gz ::: "${dirs[@]/#//path/with spaces/}"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接