在查找和循环的变量中使用 bash 扩展 globs 文件掩码
Using bash extended globs file masks in variables in find and loop
我正在尝试使用变量中的预设文件掩码来匹配文件。
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
总结结果:
tl;dr
由于 bash 的 3.x 版本中存在 错误,OP 出现意外行为 =] 与某些扩展的 glob 模式有关,即 with shopt -s extglob
in effect.
但是,即使没有错误,代码也无法按预期工作,因为 globbing 模式 *file2*?(.gz)
实际上与 *file*
相同 - 它将匹配具有 any 后缀的文件,而不仅仅是 .gz
.
只匹配包含 file2
且 或 根本没有 没有 后缀的名称,或,如果他们有[至少]一个,[最后]后缀为.gz
,使用*([^.])file2*([^.])?(*.gz)
(这在 bash 3.x 中也能正常工作)。请注意,与 OP 的模式一样,这 需要使用 shopt -s extglob
.
激活扩展的 globbing
假设OP的意图如下:
仅匹配包含 file2
[在第一个后缀之前,如果有的话] either 有 no 后缀的名称,或,如果他们有[至少]一个,[最后]后缀为.gz
例如,匹配文件 file2
file2-a
、some-file2
、file2.gz
、file2-a.gz
、file2.tar.gz
,但不匹配 file2.no
(因为它有一个不是'.gz'的[last]后缀)。
虽然 是 一个 bash 3.x 影响 *?(...)
等模式的错误 - 见下文 - 有没有充分的理由使用 *?(...)
,因为它实际上与 *
相同,因为 *
匹配 any 序列字符数,包括后缀。
下面的解决方案不受影响。
您不能使用*
来仅匹配文件名的根([first]后缀之前的部分),因为 *
匹配 任何 字符串,无论是否是后缀的一部分。
因此,必须使用扩展的 glob *([^.])
,它匹配包含任何字符的任意长度的字符串 除了 .
(一个句点)。
此外,为了说明一个文件名可能有 多个 后缀,可选的 .gz
模式匹配部分应该是 ?(*.gz)
.
放在一起:
注意: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.
bash 3.x:
中不明显的扩展通配错误
请注意,该错误与是否使用变量无关。
我不知道这个错误在哪个版本中被修复了,但是它不存在于 4.3.30,例如。
简而言之,*?(...)
错误地表现得好像已指定 *+(...)
。
换句话说:独立的简单模式*
后跟扩展模式?(...)
(匹配零或1...
实例)有效地表现像 *
后跟 +(...)
(匹配 1 个或更多 ...
个实例)。
演示,在 bash 3.2.57 中观察到(OSX 10.10.2 上的当前版本;OP 使用 3.2.25):
$ touch f f.gz # create test files
$ ls -1 f?(.gz) # OK: finds files with basename root 'f', optionally suffixed with '.gz'
f
f.gz
# Now extend the glob with `*` after the basename root.
# This, in fact, is logically equivalent to `f*` and should
# match *all files starting with 'f'*.
$ ls -1 f*?(.gz)
f.gz
# ^ BUG: only matches the suffixed file.
我正在尝试使用变量中的预设文件掩码来匹配文件。
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
总结结果:
tl;dr
由于 bash 的 3.x 版本中存在 错误,OP 出现意外行为 =] 与某些扩展的 glob 模式有关,即 with
shopt -s extglob
in effect.但是,即使没有错误,代码也无法按预期工作,因为 globbing 模式
*file2*?(.gz)
实际上与*file*
相同 - 它将匹配具有 any 后缀的文件,而不仅仅是.gz
.只匹配包含
file2
且 或 根本没有 没有 后缀的名称,或,如果他们有[至少]一个,[最后]后缀为.gz
,使用*([^.])file2*([^.])?(*.gz)
(这在 bash 3.x 中也能正常工作)。请注意,与 OP 的模式一样,这 需要使用shopt -s extglob
. 激活扩展的 globbing
假设OP的意图如下:
仅匹配包含 file2
[在第一个后缀之前,如果有的话] either 有 no 后缀的名称,或,如果他们有[至少]一个,[最后]后缀为.gz
例如,匹配文件 file2
file2-a
、some-file2
、file2.gz
、file2-a.gz
、file2.tar.gz
,但不匹配 file2.no
(因为它有一个不是'.gz'的[last]后缀)。
虽然 是 一个 bash 3.x 影响 *?(...)
等模式的错误 - 见下文 - 有没有充分的理由使用 *?(...)
,因为它实际上与 *
相同,因为 *
匹配 any 序列字符数,包括后缀。
下面的解决方案不受影响。
您不能使用*
来仅匹配文件名的根([first]后缀之前的部分),因为 *
匹配 任何 字符串,无论是否是后缀的一部分。
因此,必须使用扩展的 glob *([^.])
,它匹配包含任何字符的任意长度的字符串 除了 .
(一个句点)。
此外,为了说明一个文件名可能有 多个 后缀,可选的 .gz
模式匹配部分应该是 ?(*.gz)
.
放在一起:
注意: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.
bash 3.x:
中不明显的扩展通配错误请注意,该错误与是否使用变量无关。
我不知道这个错误在哪个版本中被修复了,但是它不存在于 4.3.30,例如。
简而言之,*?(...)
错误地表现得好像已指定 *+(...)
。
换句话说:独立的简单模式*
后跟扩展模式?(...)
(匹配零或1...
实例)有效地表现像 *
后跟 +(...)
(匹配 1 个或更多 ...
个实例)。
演示,在 bash 3.2.57 中观察到(OSX 10.10.2 上的当前版本;OP 使用 3.2.25):
$ touch f f.gz # create test files
$ ls -1 f?(.gz) # OK: finds files with basename root 'f', optionally suffixed with '.gz'
f
f.gz
# Now extend the glob with `*` after the basename root.
# This, in fact, is logically equivalent to `f*` and should
# match *all files starting with 'f'*.
$ ls -1 f*?(.gz)
f.gz
# ^ BUG: only matches the suffixed file.