如何提取大型文字檔案的一部分,從第一次出現的 FOO 開始,到第一次出現的 BAR 結束?
就我而言,我試圖提取 mysqldump 創建的 sql 文件的一部分。
答案1
致謝@dgig和@保羅誰幫我提供了回饋!最終的 perl
這裡單行:
perl -lne 'if(/FOO/../BAR/){s/.*?(FOO)/$1/ if!$i++;s/BAR\K.*//&&print&&exit;print}' file
解釋:
if(/FOO/../BAR/){ # perform the following actions on each line, starting
# with a line that contains FOO, and up to and including
# a line that contains BAR
s/.*?(FOO)/$1/ if!$i++; # only on the first line that contains FOO,
# delete all characters before FOO
s/BAR\K.*//&&print&&exit;# if the line contains BAR, remove characters
# after BAR, print the line and stop processing
print # simply print the line contents
舊答案:
致謝@保羅一個簡單的sed
解決方案。它同樣簡單易讀awk
:
awk '/FOO/,/BAR/' file
但它可能太簡單了:它會傳回整行,而不是完全「從第一次出現 FOO 到第一次出現 BAR 結束的文字的一部分」。我認為這意味著 FOO 應該是第一個詞,BAR 應該是最後一個詞。要準確地做到這一點需要一個更複雜的答案。讓我嘗試在 中實現這一點perl
。
簡單情況(返回整行):
perl -lne 'print if /FOO/../BAR/' file
複雜情況(正好從 FOO 到 BAR):
perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//;print}' file
我喜歡這個等效的解決方案,它將變數分配給範圍運算子:
perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*// if$a=~/E/;print}' file
筆記:假設只有一部分文本需要提取,即在由FOO 和BAR 分隔的第一段之後我們不應該遇到另一個FOO。
否則,簡單的情況就不再那麼簡單了awk
:
awk '/FOO/,/BAR/ {print; if ($0~/BAR/) {exit} }' file
並在perl
:
perl -lne '(print&&/BAR/&&exit) if /FOO/../BAR/' file
複雜、更精細的解決方案變成:
perl -lne 'if(/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if!$i++;$_=~s/BAR\K.*//&&print&&exit;print}' file
和:
perl -lne 'if($a=/FOO/../BAR/){$_=~s/.*?(FOO)/$1/ if$a==1;$_=~s/BAR\K.*//&&print&&exit if$a=~/E/;print}' file
這個例子展示了一句台詞如何從異常清晰和不言自明的內容變成看起來像是模糊的隨機字元序列,因為它為問題增加了一點複雜性。無論何時需要,我都會建議編寫一個獨立的、可維護的、可讀的腳本,可以輕鬆添加額外的功能並考慮極端情況。
答案2
在這種情況下,事情並沒有我想像的那麼困難。對於sed
,從第一次出現 FOO 到第一次出現 BAR (我沒有嘗試,但可能像第二個 FOO 到第二個 BAR 這樣的事情會更困難。)
sed -nr '/FOO/ {
/FOO/ s/[^F]+FOO/FOO/p
:a
n
/BAR/ s/([^B]+BAR).*/\1/
p
/BAR/ q
ba
}' <<<'line1
> line2 FOO text1 FOO text2
> line3
> line4 BAR text3 BAR text4
> line5'
FOO text1 FOO text2
line3
line4 BAR