簡體   English   中英

使用awk從目標正則表達式向前和向后輸出文件部分

[英]Use awk to output section of file forward and back from target regex

這是對使用awk / find輸出結果和文件名的擴展,在這里我發現了如何使用awk輸出文件名以及文件的一部分,該部分與開始和結束正則表達式匹配。

因此,如果我有一個包含內容的文件fileThree.txt

XXX >>
 xxx one
 xxx two
 xxx three
<<

ZZZ >>
 zzz one
 zzz two
 zzz three
<<

然后這個命令:

awk '/XXX/,/<</{print a[FILENAME]?$0:FILENAME RS $0;a[FILENAME]++}' *.txt

將輸出

/d/Temp/temp/fileTwo.txt
XXX >>
 xxx one
 xxx two
 xxx three
<<

我喜歡它,並且每天都在使用它,但是我想進一步擴展它,但是還沒有弄清楚該如何做。 本質上,我想說“在y和z之間搜索x,輸出y到z之間的所有行(包括行)。”

因此,我想搜索“ xxx two”並獲取該“塊”中的所有內容,從以“ >>”開始並以“ <<”結束-即它將具有與上述完全相同的輸出。


更新時間 :2014年1月31日,星期五,下午03:53:29

顯示@Endoro 建議結果 ,該建議輸出不正確。 命令:

awk '/xxx one/{f=7};/>>/{delete(s)};{s[++i]=$0};/<</&&f {print FILENAME;for (j in s) print s[j];f=0}' *.txt

輸出:

fileThree.txt
 xxx three
<<
XXX >>
 xxx one
 xxx two
fileTwo.txt
XXX >>
 xxx one
 xxx two
 xxx three
<<

更新 :2014年2月4日,星期日

為了回應@EdMorton的回答,這些文件僅是示例,並且通用格式是“記錄”以任何以“ >>”結尾的行開頭,並以僅包含“ <<”的任何行結尾。 這意味着記錄可以包含空白行。


更新 :2014年2月3日,星期一,上午11:49:22

在查看@EdMorton的答案時,我設計了解決方案,該解決方案可通過以下方式在腳本中使用:

# Set these based on input arguments.
ignoreCase=
searchTerm=
directory=
# Then do the search
gawk -v RS='\n<<\n+' "BEGIN{IGNORECASE=$ignoreCase} /${searchTerm}/{print FILENAME ORS \$0 ORS \"<<\"}" "${directory}"/*.txt | less -I -p "$searchTerm"

給定您發布的輸入格式,使用awk獲取所需輸出的方法是:

awk -v RS= '/xxx two/{print FILENAME ORS $0}' file

看到:

$ cat file
XXX >>
 xxx one
 xxx two
 xxx three
<<

ZZZ >>
 zzz one
 zzz two
 zzz three
<<
$
$ awk -v RS= '/xxx two/{print FILENAME ORS $0}' file
file
XXX >>
 xxx one
 xxx two
 xxx three
<<

另外,給定更新問題中的信息,記錄可以包含空行,對多字符RS使用GNU awk:

$ gawk -v RS='\n<<\n+' '/xxx two/{print FILENAME ORS $0 ORS "<<"}' file
file
XXX >>
 xxx one
 xxx two
 xxx three
<<

或(選擇):

$ gawk -v RS='\n<<' '/xxx two/{sub(/^\n+/,""); print FILENAME ORS $0 RT}' file
file
XXX >>
 xxx one
 xxx two
 xxx three
<<

或者,如果您在記錄之間沒有真正的空白行,或者確實有空白行,但是不在乎是否在輸出中復制它們:

$ gawk -v RS='\n<<\n' '/xxx two/{printf "%s", FILENAME ORS $0 RT}' file
file
XXX >>
 xxx one
 xxx two
 xxx three
<<

順便說一句,如果您必須使用non-gawk進行此操作,那么您有2種主要選擇:

1)將您的真實RS映射到單個字符:

$ awk '{sub(/<</,SUBSEP)}1' file | awk -v f=file 'BEGIN{RS=SUBSEP} /xxx two/{print f ORS $0 "<<"}'
file
XXX >>
 xxx one
 xxx two
 xxx three
<<

2)或通過連接行創建記錄字符串,例如:

$ awk '{rec = rec $0 ORS} /^<</{ if (rec ~ /xxx two/) printf "%s", FILENAME ORS rec; rec=""}' file
file
XXX >>
 xxx one
 xxx two
 xxx three
<<

無論哪種方式,您都不需要構建數組,設置標志,循環等。-始終只需標識/創建記錄並對每個記錄進行RE比較。

您可以使用測試:

awk '/xxx one/{f=7};/>>/{delete(s)};{s[++i]=$0};/<</&&f {print FILENAME;for (j in s) print s[j];f=0}' *.txt

要獲得有序的輸出,請參閱@EdMorton的注釋:

awk '/zzz one/{f=7}/>>/{delete(s);i=0}{s[++i]=$0}/<</&&f {print FILENAME;for (j=1;j<=i;j++) print s[j];f=0}' *.txt

Endoro提交時,我正在處理此問題。 我認為這在多行上更具可讀性。 Endoro解決方案與這一解決方案之間的主要區別-這一解決方案使讀取順序保持一致,並丟棄了不包含搜索文本的匹配塊:

#!/bin/sh

awk '/>>/ { p=1 }
p     { a[i++]=$0; if(/xxx two/) m=1 }
/<</  {
    if(m) {
        print FILENAME
        for( j=0; j<i; j++ ) { print a[j] }
        m=0
    }
    p=0; i=0; delete a
}' $*

通過awk塊,基本上是:

  • 開始模式
  • 當“在塊中”時,將行存儲在“索引”數組中,如果塊匹配,則設置標志
  • 在模式末尾,按順序打印出數組,然后重置變量並清除數組

這是帶有額外內容的“單行”版本; s

awk '/>>/ {p=1} p {a[i++]=$0; if(/xxx two/) m=1} /<</{if(m){print FILENAME; for(j=0;j<i;j++) {print a[j]} m=0 } p=0; i=0; delete a}' *.txt

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM