2012-10-16 35 views
9

需要幫助掃描文本文件並查找兩個模式之間的所有單詞。就像說如果我們有一個.sql文件,需要掃描並找到'和'之間的所有單詞。 Grep一次只能掃描1行。對於這個需求,什麼是最好的Unix腳本使用? sed,awk有這些功能嗎?指出任何例子,非常感謝。Grep訪問多行,查找兩個模式之間的所有單詞

+1

你能粘貼一個示例sql內容嗎?例如有多少來自...你文件中的哪個位置?有沒有在同一行中「from」和「where」的情況?所有這些使得提取邏輯不同。 – Kent

+0

這個答案也可能適用:https://stackoverflow.com/a/48022994/2026975 – imriss

回答

21

桑達具有這樣的:

sed -n -e '/from/,/where/ p' file.sql 

打印所有的線之間具有from和帶where的線行。

的東西,可以包括具有無論從哪裏線:

#!/bin/sed -nf 

/from.*where/ { 
    s/.*\(from.*where\).*/\1/p 
    d 
} 
/from/ { 
    : next 
    N 
    /where/ { 
     s/^[^\n]*\(from.*where\)[^\n]*/\1/p 
     d 
    } 
    $! b next 
} 

這(寫爲sed腳本)稍微複雜一些,我會盡力解釋的細節。

第一行在包含fromwhere的行上執行。如果一行符合該模式,則執行兩條命令。我們使用s替代命令僅提取from和where(包括from和where)之間的部分。該命令中的p後綴將打印該行。 delete命令清除模式空間(工作緩衝區),加載下一行並重新啓動腳本。

當找到包含from的行時,第二個命令開始執行一系列命令(由大括號分組)。基本上,這些命令會形成一個循環,它會將輸入中的行附加到模式空間中,直到找到包含where的行或直到我們到達最後一行。

:「命令」創建一個標籤,腳本中的一個標記,允許我們在需要時「跳回」。 N命令從輸入中讀取一行,並將其追加到模式空間(用換行符分隔行)。

當找到where時,我們可以打印出模式空間的內容,但首先我們必須使用substitute命令清理它。它與之前使用的類似,但我們現在用[^\n]*替換前導字尾.*,它告訴sed僅匹配非換行符,從而有效地匹配第一行中的from和最後一行中的from。然後d命令清除模式空間並重新啓動下一行上的腳本。

b命令將跳轉到標籤,在我們的示例中,標籤爲next。然而,$!地址表示它不應該在最後一行執行,讓我們離開循環。當以這種方式離開循環時,我們還沒有找到相應的where,所以你可能不想打印它。

但是請注意,這有一些缺點。下列情況將不會按預期處理:

from ... where ... from 

from ... from 
where 

from 
where ... where 

from 
from 
where 
where 

處理這些情況需要更多的代碼。

希望這有助於=)

+0

直截了當,但我不認爲這是什麼OP需要.... – Kent

+0

謝謝,救了我一堆RTFM'ing :-) –

+0

如果匹配的模式匹配行號的任何想法也打印在匹配行的起始行 –

2

隨着GNU awk的,所以你可以設置RS到RE:

gawk -v RS='[[:space:]]+' ' 
    /where/ { found=0 } 
    found { print } 
    /from/ { found=1 } 
' file 

上述假設你不希望「從」和「在哪裏」印刷,如果有必要的話,可以移動線路來做其他事情

萬一有幫助,下面的成語描述瞭如何選擇給出 特定模式相匹配的記錄範圍:

awk '/pattern/{f=1}f' file 

B):

一)從某種模式打印的所有記錄經過一番模式打印的所有記錄:

awk 'f;/pattern/{f=1}' file 

c)之後,一些圖案印製的第N個記錄:

awk 'c&&!--c;/pattern/{c=N}' file 

d)打印除後一些圖案中的第N個記錄中的每個記錄:

awk 'c&&!--c{next}/pattern/{c=N}1' file 

E)之後的某個圖案打印的N個記錄:

awk 'c&&c--;/pattern/{c=N}' file 

F)打印以外的每個記錄N記錄後的某種模式:

awk 'c&&c--{next}/pattern/{c=N}1' file 

g)打印N張記錄從一些模式RDS:

awk '/pattern/{c=N}c&&c--' file 

我從「F」改爲變量名的「發現」,以「C」爲「計數」,其中 適合作爲這更多表現的是什麼變量實際上是。

1

你可以使用ed這個,它允許正面和負面的正則表達式的範圍。如果輸入是:

seq 10 | tee > infile 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

管在命令ed

<<< /3/,/6/p | ed -s infile 

即打印包含36線之間的一切。

結果:

3 
4 
5 
6 

爲了得到每端一個多行:

<<< /3/-1,/5/+1p | ed -s infile 

結果:

2 
3 
4 
5 
6 
7 

或者周圍的其他方式:

<<< /3/+1,/6/-1p | ed -s infile 

結果:

4 
5 
0

只返回給定的兩個字符串中的一個字符串,沿awk線(沒有得到瘋了)我只是跑這個非常平坦的腳本,冗長拖:

.\gnucoreutils\bin\awk "{startstring = \"RETURN STUFF AFTER ME \"; endstring = \"RETURN STUFF BEFORE ME\"; endofstartstring = index($0,startstring)+length(startstring); print substr($0,endofstartstring,index($0,endstring)-endofstartstring)}" /dev/stdin 

請注意,我用的cmd.exe(在Windows命令解釋器)和the gnuwin32 awk,那麼介意「雙引號」和^ \轉義字符^ \:

GNU Awk 3.1.6 
Copyright (C) 1989, 1991-2007 Free Software Foundation. 

請指出缺點。

例如:

echo "hello. RETURN STUFF AFTER ME i get returned RETURN STUFF BEFORE ME my face is melting" | .\gnucoreutils\bin\awk "{startstring = \"RETURN STUFF AFTER ME \"; endstring = \" RETURN STUFF BEFORE ME\"; endofstartstring = index($0,startstring)+length(startstring); print substr($0,endofstartstring,index($0,endstring)-endofstartstring)}" /dev/stdin 
i get returned 
1

我能做到這一點只用grep的:

#> grep -A#### "start pattern" file | grep -B#### "end pattern" 

的問題是,我必須找到線適量的A和B,包括選項,這是相同的。 希望這可以幫助

相關問題