在find和loop中的变量中使用bash扩展的globs文件掩码

时间:2015-03-03 11:35:07

标签: bash glob

我正在尝试使用变量中的预设文件掩码来匹配文件。

mat $ ls -lQ /tmp/Mat
total 0
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:32 "testfile1"
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:33 "testfile1.gz"
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:33 "testfile2"
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:33 "testfile2.gz"
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:38 "testfile2.gz#id=142"
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:34 "testfile2test"
-rw-rw-r-- 1 Mat Mat 0 Mar  3 14:34 "testfile2test.gz"
mat $ file_mask=*file2*
mat $ ls /tmp/Mat/$file_mask?(.gz)
testfile2.gz  testfile2test.gz

我想得到:testfile2 testfile2.gz testfile2test testfile2.gz

1 个答案:

答案 0 :(得分:2)

总结结果:

<强> TL;博士

  • OP经历了意外行为,因为3.x版本的bash 中的错误与某些扩展的glob模式相关,即 with { {1}}实际上

  • 但是,即使没有错误,代码也无法正常工作,因为通配模式shopt -s extglob实际上与{{1}相同 - 这将匹配带有任何后缀的文件,而不仅仅是*file2*?(.gz)

  • 仅匹配包含<{1>} 后缀后缀的*file*的名称,如果他们有[至少]一个,[最后]后缀为.gz,使用 file2 (这也适用于bash 3.x)。请注意,与OP的模式一样,需要使用.gz激活扩展的globbing


假设OP的意图如下:

仅匹配包含*([^.])file2*([^.])?(*.gz) [在第一个后缀之前的名称]的名称,如果其中 没有后缀,,如果他们[至少]有一个,[last]后缀为shopt -s extglob

例如,匹配文件file2 .gzfile2file2-asome-file2file2.gz,但不匹配file2-a.gz(因为它的[last]后缀不是'.gz')。

虽然 bash 3.x错误会影响file2.tar.gz等模式 - 请参阅下文 - ,没有充分的理由使用file2.no,因为它假设*?(...)匹配任何字符序列,包括后缀,则与*?(...) 实际相同。
下面的解决方案受到bug的影响。

无法使用*仅匹配文件名的 root ([first]后缀之前的部分),因为*匹配任何字符串,无论是否为后缀的一部分。

因此,必须使用扩展的glob *,它匹配包含除* (句点)之外的任何字符的任何长度的字符串。

另外,考虑到文件名可能包含多个后缀这一事实,模式的可选*([^.]) - 匹配部分应为.

把它放在一起:

注意:.gz必须有效才能使命令生效。

?(*.gz)

bash 3.x中隐藏的扩展全局错误

请注意,该错误与是否使用变量无关。

我不知道修复了哪个版本的bug,但是在4.3.30 中没有出现。

简而言之, shopt -s extglob错误地表现为指定了# Create test files; note the addition of "testfile2.tar.gz", which SHOULD # match, and "testfile2.no", which should NOT match: $ touch "testfile1" "testfile1.gz" "testfile2" "testfile2.gz" "testfile2.gz#id=142" "testfile2test" "testfile2test.gz" "testfile2.tar.gz" "testfile2.no" $ ls -1 *([^.])file2*([^.])?(*.gz) testfile2 testfile2.gz testfile2.tar.gz testfile2test testfile2test.gz # The same, using a variable: $ file_mask=*([^.])file2*([^.]) # NO globbing here (no globbing in *assignments*). $ file_mask+=?(*.gz) # Extend the pattern; still no globbing. $ ls -1 $file_mask # Globbing happens here, due to unquoted use of the variable. # Same output as before. # Using a loop should work equally: for f in *([^.])file2*([^.])?(*.gz); do echo "$f"; done # Same output as before. # Loop with a variable: $ file_mask=*([^.])file2*([^.]) $ file_mask+=?(*.gz) $ for f in $file_mask; do echo "$f"; done # Same output as before.

换句话说:独立的简单模式*?(...)后面跟着扩展模式*+(...)(匹配 0或1 *实例)有效地表现得像{{1} }后跟?(...)(匹配 1个或更多 ...个实例)。

演示,在bash 3.2.57中观察到(OSX 10.10.2上的当前版本; OP使用3.2.25):

*