我正在尝试使用变量中的预设文件掩码来匹配文件。
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
答案 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
.gz
,file2
,file2-a
,some-file2
,file2.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):
*