Grep:星號 (*) 並不總是有效

Grep:星號 (*) 並不總是有效

如果我 grep 一個包含以下內容的文檔:

ThisExampleString

....對於表達式This*Stringor *String,不傳回任何內容。但是,This*會如預期返回上面的行。

表達式是否用引號引起來沒有差別。

我認為星號表示任意數量的未知字元?為什麼它只有在表達式的開頭才有效?如果這是預期的行為,我應該使用什麼來代替表達式This*String*String

答案1

星號位於常用表達意思是「匹配前面的元素0次或多次」。

在您的特定情況下grep 'This*String' file.txt,您試圖說,「嘿,grep,將我的單字匹配Thi,後跟小寫字母s零次或多次,然後是單字String」。s在 中找不到小寫字母Example,因此 grep 會忽略ThisExampleString

在 的情況下grep '*String' file.txt,您是在說“grep,匹配我在單字String”之前的空字串——實際上什麼都沒有。當然,這不是ThisExampleString應該如何閱讀。 (有其他可能的含義--你可以在有或沒有標誌的情況下嘗試這個-E--但沒有一個含義是你真正想要的。

知道這.意味著“任何單個字元”,我們可以這樣做:grep 'This.*String' file.txt。現在 grep 命令將正確讀取它:This後跟任何重複任意次數的字元(將其視為 ASCII 字元的選擇),後跟String.

答案2

*BRE 1 s、ERE 1 s 和 PCRE 1 s 中的元字元符合先前分組模式的 0 次或多次出現(如果分組模式位於*元字元之前)、先前字元類的0 次或多次出現(如果字元類是元字元之前*)或前一個字元出現 0 次或多次(如果元字元之前既沒有分組模式也沒有字元類別*);

這意味著在This*String模式中,由於*元字元前面沒有分組模式或字元類,因此該*元字元與前一個字元(在本例中為s字元)匹配 0 次或多次:

% cat infile               
ThisExampleString
ThisString
ThissString
% grep 'This*String' infile
ThisString
ThissString

若要符合任意字元出現 0 次或多次,您需要與.任意字元相符的元字元出現 0 次或多次:

% cat infile               
ThisExampleString
% grep 'This.*String' infile
ThisExampleString

BRE 和 ERE 中的元字元*始終是“貪婪的”,即它將匹配最長的匹配:

% cat infile
ThisExampleStringIsAString
% grep -o 'This.*String' infile
ThisExampleStringIsAString

這可能不是所需的行為;如果不是,您可以打開grep的 PCRE 引擎(使用 選項-P)並附加?元字符,將其放在*+元字符之後會產生改變其貪婪性的效果:

% cat infile
ThisExampleStringIsAString
% grep -Po 'This.*?String' infile
ThisExampleString

1:基本正規表示式、擴充正規表示式和Perl相容正規表示式

答案3

此處找到的解釋之一關聯:

星號「*」在正規表示式中的意義與在通配符中的意義不同;它是一個修飾符,適用於前面的單一字元或表達式,例如 [0-9]。星號與其前面的零個或多個匹配。因此[A-Z]*匹配任意數量的大寫字母,包括沒有,而[A-Z][A-Z]*匹配一個或多個大寫字母。

答案4

*作為外殼都有特殊的意義通配字元(“通配符”)並作為正規表示式元字元。您必須同時考慮兩者,但如果您引用你的正規表示式,那麼你可以防止 shell 對它進行特殊處理,並確保它將它原封不動地傳遞給grep。雖然有點概念上類似,*對 shell 的意思與對 的意思截然不同grep

第一的shell 將其*視為通配符。

你說:

表達式是否用引號引起來沒有差別。

這取決於執行該命令時您所在的目錄中存在哪些檔案。對於包含目錄分隔符號的模式/,它可能取決於整個系統中存在哪些檔案。你應該永遠引用grep--and的正規表示式單引號通常是最好的——除非你確定你沒問題九種可能令人驚訝的轉變否則 shell 會執行執行grep命令。

當 shell 遇到*不存在的字元時,它意味著“零個或多個任何字元”並且替換包含它的單字以及與模式相符的檔案名稱清單。 (以 開頭的檔案名稱.被排除—除非您的模式本身以. 或者你已經配置了你的 shell 來包含它們。通配——還有名字檔案名稱擴充路徑名擴充

其效果grep通常是第一個匹配的檔案名稱被視為正規表示式——即使對於人類讀者來說很明顯它是不是意味著作為正規表示式 - 而從 glob 自動列出的所有其他檔案名稱都被視為文件裡面用於搜尋匹配項。 (您看不到該列表 - 它以不透明的方式傳遞給grep。)您實際上永遠不希望這種情況發生。

原因是這樣的有時不是問題——至少在你的具體情況下迄今,它不是——那*將被單獨留下若以下所有條件均為真:

  1. 名稱匹配的檔案。 ……或者您已在 shell 中停用了通配符,通常使用set -f或等效的set -o noglob.但這並不常見,您可能會知道您做到了。

  2. 您使用的 shell 的預設行為是*在沒有符合的檔案名稱時不進行處理。 Bash 就是這種情況,你就是大概使用,但不是在所有 Bourne 風格的 shell 中。 (例如,流行的 shell Zsh 中的預設行為是讓 glob 執行以下任一操作:(A)展開或(二)產生錯誤。……或者您已經更改了 shell 的這種行為——不同 shell 的完成方式有所不同。

  3. 你還沒有否則告訴你的 shell 允許將 glob 替換為沒有什麼當沒有匹配的文件時,也不會在這種情況下失敗並顯示錯誤訊息。在 Bash 中,這可以透過啟用nullglob或來完成failglob 外殼選項, 分別。

有時您可以依賴#2 和#3,但很少可以依賴#1。grep當您有不同的檔案或從不同的位置運行它時,現在有效的帶有不帶引號模式的命令可能會停止工作。引用你的正規表示式,問題就消失了。

然後grep命令將其*視為量詞。

其他答案——比如那些作者:謝爾蓋·科洛迪亞茲內通過 科斯——也以不同的方式解決這個問題的這個面向。因此,我鼓勵那些尚未閱讀它們的人在閱讀本答案的其餘部分之前或之後閱讀它們。

假設確實*進入了 grep——引用應該確保——grep那麼就意味著它前面的項目可能發生任意多次,而不是必須恰好發生一次。它仍然可能發生一次。或者它可能根本不存在。或可以重複。適合的文字任何這些可能性將會被匹配。

我所說的「專案」是什麼意思?

  • 單一特點。由於b匹配文字bb*因此匹配零個或多個bs,因此ab*c匹配ac, abc, abbc,abbbc等。

    同樣,由於.匹配任何字符,.*匹配零個或多個字符1,因此a.*c匹配ac, akc, ahjglhdfjkdlgjdfkshlgc, 甚至acccccchjckhcc等。或者

  • A字元類。由於[xy]匹配xor y,[xy]*匹配零個或多個字符,其中每個字符都是xor y,因此p[xy]*q匹配pq, pxq, pyq, pxxq, pxyq, pyxq, pyyq, pxxxq,pxxyq等。

    這也適用於速記形式的字元類,如\w\W\s\S。由於\w匹配任何單字字符,\w*因此匹配零個或多個單字字符。或者

  • A團體。由於\(bar\)匹配bar,\(bar\)*匹配零個或多個bars,因此foo\(bar\)*baz匹配foobaz, foobarbaz, foobarbarbaz,foobarbarbarbaz等。

    使用-Eor-P選項,grep將正規表示式視為埃雷或者聚合酶鍊式反應分別,而不是作為布雷,然後組被包圍( )而不是\( \),因此您可以使用(bar)代替\(bar\)foo(bar)baz代替foo\(bar\)baz

man grep最後給出了 BRE 和 ERE 語法的合理易懂的解釋,並grep在開頭列出了所有接受的命令列選項。我推薦該手冊頁作為資源,並且GNU Grep 文檔本教學/參考站點(我已經連結到上面的許多頁面)。

為了測試和學習grep,我建議使用模式但不使用檔案名稱來呼叫它。然後它從您的終端獲取輸入。輸入行;回顯給您的行是包含您的模式匹配的文字的行。若要退出,請在行首按Ctrl+ ,這表示輸入結束。 D(或者您可以像大多數命令列程式一樣按Ctrl+ 。)例如:C

grep 'This.*String'

如果使用--colorflag,grep會反白顯示具體的部分與正規表示式相符的行,這對於弄清楚正規表示式的作用以及在完成後查找您要查找的內容都非常有用。預設情況下,Ubuntu 用戶有一個 Bash 別名,grep --color=auto當您從命令列運行時,該別名足以滿足此目的grep,因此您可能甚至不需要--color手動傳遞。

1 因此,.*正規表示式中的意義與*shell glob 中的意義相同。但是,不同之處在於grep自動列印包含您的匹配項的行任何地方在它們中,所以通常不需要.*在正規表示式的開頭或結尾。

相關內容