Bash 模式通過“顯式”而不是使用“shopt -s dotglob”來匹配名稱以點(句點)開頭的目錄?
在構造一個匹配文件名(例如 )的模式時
/home/user/project/.git
,如何.
“顯式地”匹配字元——也就是說,不使用shopt -s dotglob
?https://www.gnu.org/software/bash/manual/html_node/Filename-Expansion.html上的手冊指出:
當模式用於文件名擴展時,字元 ‘.’ 除非設置了 shell 選項 dotglob,否則必須顯式匹配文件名的開頭或斜杠之後。
“明確匹配”究竟是什麼意思?
同樣,在http://www.tldp.org/LDP/abs/html/globbingref.html(在最後的
Notes
部分),同樣的概念得到解決:文件名擴展可以匹配點文件,但前提是模式明確包含點作為文字字元。
該說明提供了以下範例:
~/[.]bashrc # Will not expand to ~/.bashrc ~/?bashrc # Neither will this. # Wild cards and metacharacters will NOT #+ expand to a dot in globbing. ~/.[b]ashrc # Will expand to ~/.bashrc ~/.ba?hrc # Likewise. ~/.bashr* # Likewise.
我無法理解最後三個範例的內部工作原理,這些範例將擴展為包括“dotfile”。
具體來說,如何在範例中將其
b
放在括號後.
使其成為“顯式”匹配~/.[b]ashrc
?隨後的例子對我來說更加模棱兩可。我只是無法理解以與角色完全無關的方式操縱模式如何.
導致模式產生匹配。關於我為什麼要避免使用
shopt -s dotglob
,這個問題的推動力源於我正在編寫這些模式以在另一個程序的配置文件中使用。我想排除包含例如“隱藏.git
目錄”的路徑,並且我不確定我是否有能力以dotglob
任何身份指定。本質上:
.
通過“顯式”匹配字元的最簡單方法是什麼?將下一個字元放在括號中“使它起作用”,但我想知道為什麼;我覺得我用這種方法“在黑暗中射擊”。非常感謝任何關於這方面潛在行為的解釋。
編輯添加:
最初,它似乎並不相關,但因為人們似乎對我的案例的細節感興趣,所以我將進一步解釋。
我正在使用一個名為
Samhain
. 每當根據某些使用者指定的配置參數修改文件系統時,Samhain 都會“發出警報”。我不希望 Samhain 在
.git
創建/修改/刪除目錄中的文件(位於某些父目錄中)時發出警報。在 Samhain 中,這種類型的排除是通過定義“忽略規則”來執行的。這些規則的確切規範在http://www.la-samhna.de/samhain/manual/filedef.html中進行了解釋4.2. File/directory specification
。簡而言之:
Wildcard patterns ('*', '?', '[...]') as in shell globbing are supported for paths. The leading '/' is mandatory.
因此,我正在嘗試編寫一個“忽略規則”來匹配相關
.git
目錄,這實際上會導致 Samhain 將它們排除在其監控活動之外。最初,我嘗試了這個:
[IgnoreAll] dir = -1/home/user/project/*/*/.git
這沒有用;
.git
每當這些目錄中的文件發生更改時,Samhain 仍然會發出警報。在找到上面引用的範例後,我嘗試了這個:
dir = -1/home/user/project/*/*/.[g]it
通過此更改,Samhain 會根據需要忽略文件。
在發布這個問題時,我只是想了解為什麼該更改會產生預期效果。
我會說,考慮到我一開始嘗試使用的模式確實與
.git
我使用“echo”測試時所討論的目錄匹配,我覺得不那麼愚蠢:echo /home/user/project/*/*/.git
所以,我對 Bash 中的模式匹配、萬用字元或文件名擴展的一些基本知識並沒有誤解。相反,Samhain 在這種情況下如何實現模式匹配似乎存在細微差別。
我不知道為什麼在 Samhain 的配置文件的上下文中應用時這不起作用(顯然)。鑑於此編輯,也許有人能夠解釋。
首先,我假設您知道路徑名模式中的
[b]
,?
和*
含義。(如果你不這樣做,做更多的研究。)冒著重複別人所說的話的風險,你想多了。包含字元串的模式
/.
(即 a後跟 a )顯式/
地**.
包含點作為文字字元。關鍵是[b]
,?
和/或在 不影響模式是否可以匹配點文件之後*
發生。最後三個範例是作為模式範例提供的 (即,不僅僅是一個普通的文件/路徑名,而是可能匹配多個文件/路徑名的東西 - 或者沒有)將匹配- 而前兩個則匹配,如果沒有特別處理。.
~/.bashrc``~/.bashrc
.
**那麼,你真正的問題是什麼?
…我正在編寫這些模式以在另一個程序的配置文件中使用。我想排除包含例如“隱藏
.git
目錄”的路徑,並且我不確定我是否有能力以dotglob
任何身份指定。我猜你想對所有文件/目錄做一些事情(比如
chown
或 ),除了那些以點開頭的文件/目錄。但是您的程式碼將在其他人的腳本中使用(通過or命令),並且您害怕這樣做, 因為腳本可能已設置,因此會擴展到所有文件,包括“隱藏”文件。而且您不想關閉, 因為您不想破壞現有腳本的功能。cp
.
source``*your_command* *``dotglob``*``dotglob
- 使用更智能的萬用字元(路徑名擴展模式)。
我希望你能理解萬用字元(又名 glob)
[abc]
——它們匹配任何字元a
,b
或c
. 例如,字元串c[aou]t
匹配cat
,cot
和cut
;d[iou]g
匹配dig
,dog
和dug
. (它們可以並且通常與範圍一起使用;例如,[a-z]
和[0-9]
。)好吧,這種情況的一個特例是——它匹配除,或之外的任何字元。因此,您可以使用(或) 來匹配以點以外的字元開頭的名稱。矛盾的是,(在文件名的開頭)如果未設置,則不會匹配點,但會排除[**!**abc]
a``b``c``[!.]*``*directory_name*/[!.]*``[.]``dotglob``[!.]
一個點,無論 的設置如何dotglob
。
dotglob
無論是否設置,這將給出相同的結果。 2. 使用dotglob
(在子shell中)。Shell 選項 (
shopt
s) 對於程序來說是本地的,並且程序屬性永遠不會從子程序向後(上坡)流向父程序。所以(商店 -u dotglob;*your_command* *)
將*
your_command
*僅在非隱藏文件上執行,而不會影響腳本其餘部分的設置和行為。 3. 使用dotglob
(不使用子shell)。有些人喜歡避免使用子shell,因為它們使用額外的資源。但是成本微乎其微(除非您在循環中執行多次),所以這不是一個很好的理由。避免使用 subshell 的一個更好的理由是,如果您需要做一些影響 shell 環境的事情,比如
cd
orumask
。如果這是您的情況,您可以暫時關閉
dotglob
,然後再恢復之前的設置。如果您鍵入
shopt dotglob
(不帶-s
或-u
),它會報告(顯示)該dotglob
選項的目前設置。(shopt
沒有參數列出所有選項的目前設置*。**)*它還相應地設置退出狀態。-q
標誌抑制顯示,所以你可以做商店-q dotglob dotglob_setting=$? 商店-u dotglob *你的命令** 如果 [ "$dotglob_setting" = 0 ] 然後 shopt -s dotglob 是
但是等等……你說的是“另一個程序的配置文件”。你在說什麼?如果您正在談論編寫或修改類似 的文件
ignore=*.o
,那麼整個問題就沒有意義,因為該文件將由處理它的任何程序處理(和解釋),並且該程序**將決定如何解釋*
- 外殼與它無關。好的,現在我們對問題是什麼有了更好的了解:
簡短的回答是,您看到的行為沒有意義。如果
.git
目錄存在,則將其準確(字面意思)指定為.git
並使用萬用字元 / glob 模式指定它的.[g]it
行為應該相同。更長的答案:我支持我的答案第一版的最後一段。Samhain 正在讀取和解析其策略配置文件。它可能會使用 shell 來解釋配置文件中的萬用字元,但我猜它是在內部進行的。
而且,如果它是“使用外殼”,它使用的是哪個外殼?在許多系統上,
/bin/sh
不是 bash。它們在路徑名擴展模式(即萬用字元)方面的基線行為應該是相同的,但是一旦你走出門廊,你就陷入了困境。shell 的 POSIX 規範甚至沒有shopt
命令,並且(AFAIK)沒有任何方法可以*
擴展所有文件(而不僅僅是非隱藏文件)。如果你覺得
浪費花更多時間在這方面,您可能會嘗試放入/home/user/project/*
Samhain 配置文件並查看它是否將其解釋為所有文件或只是非隱藏文件。如果將其解釋為所有文件,我們可以得出結論
- Samhain 不
/bin/sh
用於擴展萬用字元。- 它沒有使用萬用字元的標準預設規則(您在問題中如此冗長地討論的規則)。
- 該文件是錯誤的(或者,充其量是不完整和具有誤導性的),因為它說:“萬用字元模式(’*’,’?’,’$$ … $$’) 就像在 shell 中一樣,路徑支持萬用字元。” 不用說(與 shell 的預設行為不同)
*
意味著所有文件。- 它可能在
dotglob
模式下使用 bash 來擴展萬用字元。但這沒有意義;正如我所說,處理.git
and.[g]it
不符合我所知道的任何 shell 的正常行為。它幾乎肯定有自己的萬用字元程式碼。但無論如何,我相信我們可以肯定地說您的結論是正確的:Samhain 在處理
IgnoreAll
規範中的萬用字元方面存在錯誤。您可能想向供應商送出錯誤報告。或者,既然您已經找到了解決方法,您就可以忘記它。
當模式用於文件名擴展時,字元 ‘.’ 除非設置了 shell 選項 dotglob,否則必須顯式匹配文件名的開頭或斜杠之後。
這只是意味著 glob
*
、?
和[...]
不匹配.
文件名開頭的 a 。如果要.
在文件名的開頭匹配 a,則不能使用 glob,必須.
顯式鍵入。例如:$ echo ???? Work $ echo .??? .gem .pki .ssh .vim
並回答您的另一個問題:
具體來說,如何在範例中將其
b
放在括號後.
使其成為“顯式”匹配~/.[b]ashrc
?僅僅因為您使用的是 glob 模式並不意味著整個模式不再是“顯式”的。
~/.[b]ashrc
例如,在 中,字元都是/.ashrc
顯式匹配的。但是,[b]
它是一個 glob 模式,也不是顯式匹配。(從技術上講,~
這是一個波浪號擴展並且比 glob 擴展更早執行,因此它也是一個顯式匹配。)但是其他字元,包括.
,確實顯式匹配,這就是為什麼~/.[b]ashrc
匹配~/.bashrc
。為了比較,
~/?[b]ashrc
將不匹配~/.bashrc
,因為.
不再顯式匹配。