括号内的Bash参数扩展不能按预期工作

括号内的Bash参数扩展不能按预期工作

问题描述:

我正在编写一个脚本,它包装find命令以搜索给定目录下的特定源文件类型。示例调用为:括号内的Bash参数扩展不能按预期工作

./find_them.sh --java --flex --xml dir1 

上述命令将搜索dir1下的.java,.as和.xml文件。

要手动为此,我想出了以下find命令:

find dir1 -type f -a \(-name "*.java" -o -name "*.as" -o -name "*.xml" \) 

由于我的脚本,我希望能够指定不同的文件,这样将搜索你最终得到的结构如下:

find_cmd_file_sets=$(decode_file_sets) # Assume this creates a string with the file sets e.g. -name "*.java" -o -name "*.as" etc 
dirs=$(get_search_dirs) # assume this gives you the list of dirs to search, defaulting to the current directory 

for dir in $dirs 
do 
    find $dir -type f -a \($find_cmd_file_sets \) 
done 

上述脚本并不像预期的那样,你执行脚本并find命令返回没有结果之前,搅动了一段时间。 我确定我创建的decode_file_setsget_search_dirs的等效项正在生成正确的结果。

一个简单的例子,如果执行直接在bash下面的shell

file_sets=' -name "*.java" -o -name "*.as" ' 
find dir -type f -a \($file_sets \) # Returns no result 
# Executing result of below command directly in the shell returns correct result 
echo find dir -type f -a \\\($file_sets \\\) 

我不明白为什么在find命令的括号变量扩展将改变这一结果。如果它有什么区别,我在Windows下使用git-bash。

这真令人沮丧。任何帮助将非常感激。最重要的是,我想了解为什么$file_sets的变量扩展是如此。

+1

在命令行? – 2014-11-05 10:26:17

+0

我不知道你会怎样认为我相信。我使用单引号,因此变量中的双引号在作为参数使用时应该保留 – ahjmorton 2014-11-05 10:38:32

+1

我想在for循环中有一个错误 - 它应该是***代替$ dirs中的dir *** – Jdamian 2014-11-05 10:52:01

TLDR:不要叫findfind_cmd_file_sets变量和禁用路径扩展(set -f)使用引号。

当你有“特殊”字符变量的内容,然后试图展开不带引号比bash的变量将围绕以“特”字用单引号,例如,每个字:

#!/usr/bin/env bash 
set -x 
VAR='abc "def"' 
echo $VAR 

的输出是:

+ VAR='abc "def"' 
+ echo abc '"def"' 
abc "def" 

正如你可以看到,庆典包围"def"单引号。在你的情况下,调用find命令变为:

find ... -name '"*.java"' ... 

所以它试图找到与"启动文件,并与.java"

最终为了防止这种行为,你唯一可以做的(我知道的)是在扩展变量时使用双引号,例如:

#!/usr/bin/env bash 
set -x 
VAR='abc "def"' 
echo "$VAR" 

输出是:

+ VAR='abc "def"' 
+ echo 'abc "def"' 
abc "def" 

唯一的问题,因为你也许已经注意到,就是现在整个变量是在引号和被视为一个参数。所以这在你的find命令中不起作用。

剩下的唯一选择是不使用引号,既不在变量内容中,也不在扩展变量时使用引号。不过,当然,你有路径扩展的一个问题:

#!/usr/bin/env bash 
set -x 
VAR='abc *.java' 
echo $VAR 

输出是:

+ VAR='abc *.java' 
+ echo abc file1.java file2.java 
abc file1.java file2.java 

幸运的,你可以使用set -f禁止路径扩展:

#!/usr/bin/env bash 
set -x 
VAR='abc *.java' 
set -f 
echo $VAR 

输出是:

+ VAR='abc *.java' 
+ set -f 
+ echo abc '*.java' 
abc *.java 

综上所述,下面应该工作:

#!/usr/bin/env bash 

pattern='-name *.java' 
dir="my_project" 
set -f 
find "$dir" -type f -a \($pattern \) 
+0

接受此,因为它是解决方案的最完整描述。 – ahjmorton 2014-11-10 10:12:44

希望这会工作,它在bash上进行测试。

file_sets=' -name "*.java" -o -name "*.as" ' 
command=`echo "find $dir -type f -a \($file_sets \)"` 

eval $command 
+0

这种方法确实有效,想知道为什么原始版本没有像预期的那样运行 – ahjmorton 2014-11-05 11:11:17

+0

不知道究竟是什么,但在我看来还是至少我所得出的结论是由于查找命令功能而将一切作为其参数而不是替代变量到它的价值。考虑变量作为它的参数 - 一个选项。 – 2014-11-05 11:18:06

+0

命令替换中的'echo'是多余的; 'command =“find $ dir -type -f -a \($ file_sets \)”'也适用。 – chepner 2014-11-05 14:11:43

bash阵列进行了介绍,让这种嵌套引用的:为什么你认为在一个变量的报价是一样的报价

file_sets=(-name "*.java" -o -name "*.as") 
find dir -type f -a \("${file_sets[@]}" \)