2015-03-03 33 views
1

我想在變量中使用預設文件掩碼來匹配文件。在查找和循環變量中使用bash擴展globs文件掩碼

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

'(GZ)'是嗎?告訴我們你的意圖... – 2015-03-03 12:22:33

+1

這是一個擴展的glob。它意味着可以匹配一個'.gz'後綴。 – 2015-03-03 12:27:53

+1

你在這裏遇到'failglob'的'不匹配'錯誤嗎? – 2015-03-03 12:29:33

回答

2

總結結果:

TL;博士

  • OP經歷了意外的行爲由於bug 3.x版本的bash涉及某些擴展的全局模式,即shopt -s extglob有效

  • 然而,即使沒有錯誤,代碼不工作打算,因爲文件名匹配模式*file2*?(.gz)實際上是相同的*file* - 這將匹配文件與任何後綴,而不只是.gz

  • 向含有file2要麼沒有後綴可言,,如果它們具有(至少)一個,具有.gz一個[最後]後綴只有匹配的名字,使用*([^.])file2*([^.])?(*.gz)(這也適用於bash 3.x)。請注意,與OP的模式一樣,此需要使用shopt -s extglob激活擴展匹配


的假設是OP的意圖如下:

比賽只包含名稱file2 [第一後綴之前,如果有的話]認爲要麼沒有後綴,如果它們[至少]一個,並且[後]後綴爲.gz

例如,匹配文件file2file2-asome-file2file2.gzfile2-a.gzfile2.tar.gz,但不file2.no(因爲它有一個[最後]後綴不是 '廣州')。

雖然一個bash 3.x的錯誤,影響的模式,如*?(...) - 因爲*比賽沒有很好的理由使用*?(...),因爲它是有效的和上面一樣*, - 見下文任何字符序列,包括後綴
下面的解決方案是而不是受該錯誤影響。

不能使用*匹配只有一個文件名(在[第一]後綴之前的部分),因爲*匹配任何字符串,是否一個後綴的一部分或沒有。

因此,擴展的水珠*([^.])必須被使用,其包含任何字符除了.(週期)的任何長度的字符串匹配。

此外,考慮到一個事實,即一個文件名可具有多個後綴,圖案的可選.gz -matching部分應該是?(*.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. 
+0

你可以明白,我可以看到你的更強大的解決方法將如何工作 - 我將如何在for循環中使用它?'for $ glob中的文件; do #something; done' – Mat 2015-03-05 09:25:41

+0

@Mat:我已經大幅改寫回答和我想我找到了合適的解決方案。總之:由於邏輯原因,您需要使用不同的擴展模式,幸運的是,這也可以繞過bug。 – mklement0 2015-03-05 16:49:14

+0

Thanks @ mklement0非常有用,您的解決方案似乎非常強大。理想情況下,如果我能更新bash,b這工作正常,直到那時:) – Mat 2015-03-06 17:17:31