Bash:用文件名中的空格扩展大括号和全局?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14381711/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-18 04:16:47  来源:igfitidea点击:

Bash: Expand braces and globs with spaces in filenames?

bashfilenamesglob

提问by Jay Hacker

I have some files that look like:

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

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

And I need all files matching the glob under a subset of the a,b,cdirs to end up as arguments to a single command:

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

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

The directories I care about come in as command line params and I have them in an array:

我关心的目录作为命令行参数进来,我把它们放在一个数组中:

dirs=( "$@" )

And I want to do something like:

我想做类似的事情:

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

but this doesn't work, because bash expands braces before variables. I have tried tricks with echoand lsand even eval(*shudder*) but it's tough to make them work with spaces in filenames. finddoesn't seem to be much help because it doesn't do braces. I can get a separate glob for each dir in an array with:

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

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

but then bash quotes the wildcards on expansion.

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

So: is there an elegant way to get all the files matching a variable brace and glob pattern, properly handling spaces, or am I stuck doing for loops? I'm using Bash 3 if that makes a difference.

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

采纳答案by Jay Hacker

Okay, so here is one using bash for the "braces" and findfor the globs:

好的,这里有一个使用 bash 来处理“大括号”和findglobs:

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

Useful with thisif you need the results in an array.

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

回答by Shawn Chin

To perform brace expansion and globbing on a path with spaces, you can quote the portions of the path that contain spaces, e.g.

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

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

Doing brace expansion using a list of values from a variable is a little tricky since brace expansion is done before any other expansion. I don't see any way but to use the dreaded eval.

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

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

P.S. In such a case however, I would personally opt for a loop to build the argument list rather than the approach shown above. While more verbose, a loop will be a lot easier to read for the uninitiated and will avoid the need to use eval(especially when the expansion candidates are derived from user input!).

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



Proof of concept:

概念证明:

Using a dummy command (x.sh) which prints out the number of arguments and prints out each argument:

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

[me@home]$ shopt -s nullglob  # handle case where globbing returns no match

[me@home]$ ./x.sh 'path with space'/{a,b}/*.txt
Number of arguments = 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"
Number of arguments = 3
- path with space/a/1.txt
- path with space/b/2.txt
- path with space/b/3.txt

回答by Jay Hacker

Here's one for the GNU Parallelfans:

这是GNU Parallel粉丝的一个:

parallel -Xj1 mycmd {}/*.gz ::: "${dirs[@]/#//path/with spaces/}"