如何将 glob 表达式分配给 Bash 脚本中的变量?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/369145/
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
How to assign a glob expression to a variable in a Bash script?
提问by kdgregory
When the following two lines of code are executed in a bash script, "ls" complains that the files don't exist:
当在 bash 脚本中执行以下两行代码时,“ls”会抱怨文件不存在:
dirs=/content/{dev01,dev02}
ls -l $dirs
When I run the script with the -x option, it appears to be passing the variable within single quotes (which would prevent globbing):
当我使用 -x 选项运行脚本时,它似乎在单引号内传递变量(这将防止通配):
+ dirs=/content/{dev01,dev01}
+ ls -l '/content/{dev01,dev01}'
ls: /content/{dev01,dev01}: No such file or directory
If I execute the "ls" command from my interactive shell (sans quotes), it returns the two directories.
如果我从交互式 shell(无引号)执行“ls”命令,它会返回两个目录。
I've been reading through the Bash Reference Manual (v 3.2) and can't see any reason for filename globbing to not take place (I'm not passing -f to the shell), or anything that I can set to ensure that globbing happens.
我一直在阅读 Bash 参考手册 (v 3.2) 并且看不出任何不发生文件名通配的原因(我没有将 -f 传递给 shell),或者我可以设置的任何内容以确保发生通配。
采纳答案by Johannes Schaub - litb
I think it is the order of expansions:
我认为这是扩展的顺序:
The order of expansions is:
brace expansion
, tilde expansion, parameter,variable
and arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, andpathname expansion
.
扩展的顺序是:
brace expansion
、波浪号扩展、参数variable
和算术扩展和命令替换(以从左到右的方式完成)、分词和pathname expansion
.
So if your variable is substituted, brace expansion doesn't take place anymore. This works for me:
因此,如果您的变量被替换,则不再发生大括号扩展。这对我有用:
eval ls $dirs
Be very careful with eval. It will execute the stuff verbatimly. So if dirs contains f{m,k}t*; some_command
, some_command will be executed after the ls finished. It will execute the string you give to eval
in the current shell. It will pass /content/dev01 /content/dev02
to ls, whether they exist or not. Putting *
after the stuff makes it a pathname-expansion, and it will omit non-existing paths:
使用 eval 时要非常小心。它将逐字执行这些东西。所以如果 dirs 包含f{m,k}t*; some_command
, some_command 将在 ls 完成后执行。它将执行您eval
在当前 shell 中提供的字符串。它将传递/content/dev01 /content/dev02
给 ls,无论它们是否存在。把*
东西放在后面使它成为路径名扩展,它会省略不存在的路径:
dirs=/content/{dev01,dev02}*
I'm not 100% sure about this, but it makes sense to me.
我不是 100% 确定这一点,但这对我来说很有意义。
回答by feoh
回答by Yoni Roit
This isn't filename globbing, this is brace expansion. The difference is subtle, but it exists - in filename globbing you would only receive existing files as a result, while in brace expansion you can generate any kind of string.
这不是文件名通配,这是大括号扩展。差异是微妙的,但它存在 - 在文件名通配中,您只会收到现有文件,而在大括号扩展中,您可以生成任何类型的字符串。
http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion
http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion
http://www.gnu.org/software/bash/manual/bashref.html#Filename-Expansion
http://www.gnu.org/software/bash/manual/bashref.html#Filename-Expansion
Now, this is what worked for me:
现在,这对我有用:
#!/bin/sh
dirs=`echo ./{dev01,dev02}`
ls $dirs
回答by cxw
For folks (like me) finding this through Google, @Peter and @feoh's answers are the general solution to "How to glob variables in bash script".
对于通过谷歌找到的人(像我一样),@Peter 和 @feoh 的答案是“如何在 bash 脚本中全局变量”的一般解决方案。
list_of_results=(pattern)
will save existing filenames matching pattern
into the array list_of_results
. Each element of list_of_results
will hold one filename, spaces and all.
将匹配的现有文件名保存pattern
到数组中list_of_results
。的每个元素list_of_results
将包含一个文件名、空格和所有内容。
You can access each result as "${list_of_results[<index>]}"
for <index>
starting from 0. You can get the entire list, properly quoted, as "${list_of_results[@]}"
.
您可以从 0 开始访问每个结果"${list_of_results[<index>]}"
。<index>
您可以获得整个列表,正确引用,如"${list_of_results[@]}"
.
回答by Peter
I suspect that what you need is an array, but that will restrict you to newer bashes. It is saver than using eval.
我怀疑您需要的是一个数组,但这会限制您使用较新的 bash。它比使用 eval 更节省。
dirs=( /"content with spaces"/{dev01,dev02} )
dirs=( /content/{dev01,dev02} )
ls -l "${dirs[@]}"
/content/{dev01,dev02}
will expand to:
将扩展到:
"/content/dev01" "/content/dev02"
The existence of those directories is irrelevant to the expansion.
这些目录的存在与扩展无关。
It becomes unpredictable when you assign a variable to a brace expansion.
当您将变量分配给大括号扩展时,它变得不可预测。
dirs=/content/{dev01,dev02}
may turn into
可能会变成
"/content/dev01"
or
或者
"/content/dev01 /content/dev02"
or
或者
"/content/dev01" "/content/dev02"
or
或者
"/content/{dev01,dev02}"
If you quote the braces in any way they will not expand, so the result will contain the braces and be mostly meaningless.
如果您以任何方式引用大括号,它们将不会扩展,因此结果将包含大括号并且几乎毫无意义。
回答by gniourf_gniourf
Since you want to glob files,you shouldn't use brace expansions. Using brace expansion in this case is an antipatternand definitely the wrong tool for the job.
由于您想要 glob文件,您不应该使用大括号扩展。在这种情况下使用大括号扩展是一种反模式,绝对是该工作的错误工具。
What you want is extended globbing:
你想要的是扩展通配符:
shopt -s extglob # likely already set in interactive shells
dirs=/content/@(dev01|dev02)
ls $dirs
回答by Zsolt Botykai
ls `echo $dirs`
works under cygwin.
在 cygwin 下工作。
回答by Sam Liddicott
The issue which no-one has addressed is that the variable assignment makes the difference.
没有人解决的问题是变量赋值产生了差异。
dirs=/content/{dev01,dev02}
expands differently than
扩展不同于
echo /content/{dev01,dev02}
The question is how to assign the results of the expansion to dirs
问题是如何将扩展的结果分配给 dirs