如何将 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

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

How to assign a glob expression to a variable in a Bash script?

bashglob

提问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, variableand arithmetic expansion and command substitution (done in a left-to-right fashion), word splitting, and pathname 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 evalin the current shell. It will pass /content/dev01 /content/dev02to 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

Hereis an excellent discussion of what you are trying to do.

是关于您正在尝试做什么的精彩讨论。

The short answer is that you want an array:

简短的回答是你想要一个数组:

dirs=(/content/{dev01,dev01})

But what you do with the results can get more complex than what you were aiming for I think.

但是你对结果所做的事情可能比你的目标更复杂。

回答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 patterninto the array list_of_results. Each element of list_of_resultswill 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

See: How to use Bash substitution in a variable declaration

请参阅:如何在变量声明中使用 Bash 替换