Text-Processing

如何在非常長的行的非常大的文件中搜尋字元串?

  • November 17, 2021

原來我grep昨天不小心用錯了。我剛剛檢查了我的 bash 歷史記錄並看到了我正在執行的操作:

grep search-string-here -f large-file-with-long-lines.txt

這就是導致記憶體耗盡的原因。

執行:

grep search-string-here large-file-with-long-lines.txt

…具有所需的行為。

感謝@αғsнιη 指出有類似錯誤的問題,感謝@EdMorton 和@ilkkachu 糾正我對行長以及如何grep使用awk記憶體的假設。

以下是最初的問題(儘管我認為長線無法放入 8 GB 的 RAM 似乎是錯誤的)和 @EdMorton 接受的答案。

我有一個非常大的文件(超過 100 GB),其中的行很長(甚至無法放入 8 GB RAM),我想在其中搜尋一個字元串。我知道grep不能這樣做,因為grep試圖將整行放入記憶體。

到目前為止,我想出的最佳解決方案是:

awk '/search-string-here/{print "Found."}' large-file-with-long-lines.txt

我實際上對這個解決方案很滿意,但我只是想知道是否有一些更直覺的方法可以做到這一點。也許其他一些實現grep

這會很慢,因為它會多次讀取輸入文件(您要查找的字元串中的每個字元一次,每次在字元串中該搜尋字元串的大小)但它應該適用於任何大小的文件,任何大小的行,將 GNU awk 用於多字元RSRT(未經測試):

awk -v str='search-string-here' '
   BEGIN {
       lgth = length(str)
       RS = sprintf("%*s",lgth,"")
       gsub(/ /,".",RS)
       for (i=1; i<lgth; i++) {
           ARGV[ARGC++] = ARGV[1]
       }
   }
   RT == str {
       print "found"
       exit
   }
' file

它將 RS 設置為 N .s,其中 N 是搜尋字元串的長度,從 char #1 開始從輸入中讀取每個 N 個字元鏈,將它們與搜尋字元串進行比較,如果輸入中的目前 N 個字元與搜尋匹配則退出細繩。如果該通道沒有匹配,它會再次執行相同的操作,但從 char #2 開始,依此類推,直到完成 N 次,因此輸入文件中沒有更多長度為 N 的字元串可與搜尋字元串進行比較。

請注意,上面是進行字元串比較,而不是正則表達式比較。要進行正則表達式比較,您必須以其他方式確定匹配字元串的最大長度,然後使用正則表達式比較運算符~而不是==,例如,如果您知道輸入中的匹配字元串不能超過 20 個字元,那麼您可以執行以下操作:

awk -v regexp='search-regexp-here' '
   BEGIN {
       lgth = 20
       RS = sprintf("%*s",lgth,"")
       gsub(/ /,".",RS)
       for (i=1; i<lgth; i++) {
           ARGV[ARGC++] = ARGV[1]
       }
   }
   RT ~ regexp {
       print "found"
       exit
   }
' file

但是那個正則表達式搜尋有一些字元串搜尋沒有你必須考慮的缺陷,例如,如果你的搜尋正則表達式包括^$邊界,那麼你可能會在上面得到一個錯誤的匹配,因為它會在每個字元周圍創建字元串開始/結束邊界因為正在讀取 N 個字元長的字元串。

這是一個簡單的部分解決方案,它僅適用於沒有出現在要搜尋的字元串(或正則表達式)中的字元,但**在行上出現的頻率足夠高,**因此該字元出現之間的間隔始終適合記憶體。例如,假設每一行都是一個很長的列表,由相對較短的分號分隔的欄位組成。

<large-file-with-long-lines.txt tr ';' '\n' | grep 'search-string-here'

這是一個不同的部分解決方案,如果出現總是在行的開頭之後的 N 個字元的倍數處開始,對於一些固定的 N。它用於fold換行和ag進行多行搜尋。在此範例中,已知事件總是在行首之後開始 3*x 個字元。

<large-file-with-long-lines.txt fold -w3 | ag $'cat\ntag\ngag\nact'

這可以通過重複搜尋每個偏移量來推廣到任意字元串搜尋。

<large-file-with-long-lines.txt fold -w3 | ag $'fee\n-fi\n-fo\n-fu\nm|fe\ne-f\ni-f\no-f\num|f\nee-\nfi-\nfo-\nfum'

請注意,如果字元串碰巧幾乎存在,但中間有換行符,這可能會產生誤報。

引用自:https://unix.stackexchange.com/questions/677767