web-dev-qa-db-ja.com

AWKを使用して***で区切られた段落を抽出する

以下のようなファイルがあります:

blablabla
blablabla
***
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

blablabla
blablabla

thingsIwantToReadで段落を抽出したいと思います。そのような問題に対処しなければならなかったとき、私は [〜#〜] awk [〜#〜] を次のように使用しました:

awk 'BEGIN{ FS="Separator above the paragraph"; RS="" } {print $2}' $file.txt | awk 'BEGIN{ FS="separator below the paragraph"; RS="" } {print $1}'

そしてそれはうまくいった。

この場合、FS="***""\*{3}""\*\*"(AWKは通常のアスタリスクのように処理するため機能しません)、"\\*\\*"、または可能なあらゆる正規表現を配置しようとしました考えてみてください、しかしそれは機能していません(それは何も印刷していません)。

なぜなのかご存知ですか?

そうでない場合は、私の問題に対処する別の方法を知っていますか?

解析したいファイルの抜粋の下:

13.2000000000     , 3*0.00000000000       ,  11.6500000000     , 3*0.00000000000       ,  17.8800000000

Blablabla

  SATELLITE EPHEMERIS
     ===================
Output frame: Mean of J2000

       Epoch                  A            E            I           RA           AofP          TA      Flight Ang
*****************************************************************************************************************
2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311
 <np>
 ----------------
 Predicted Orbit:
 ----------------

 Blablabla

そして私は抽出したいです:

2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311

そして、*の行の後の数字を取得するために使用しようとしたコマンド:

`awk 'BEGIN{ FS="\\*{2,}"; RS="" } {print $2}' file | awk 'BEGIN{ FS="<np>"; RS="" } {print $1}'`
4
JoVe

2つの区切り文字の間で印刷するようにawkに指示します。具体的には:

awk '/\*{4,}/,/<np>/' file

これは区切り文字を含む行も出力するので、次のようにしてそれらを削除できます:

awk '/\*{4,}/,/<np>/' file | tail -n +2 | head -n -1

または、行が1番目の区切り文字と一致する場合は変数をtrueに設定し、2番目の区切り文字と一致する場合はfalseに設定し、trueの場合にのみ出力することができます。

awk '/\*{4,}/{a=1; next}/<np>/{a=0}(a==1){print}' file

上記のコマンドは、現在の行が4つ以上の*に一致する場合、aを1に設定し、next行にもスキップします。これは、***行が印刷されないことを意味します。


これは、元の誤解されたバージョンの質問への回答でした。少し異なる状況で役立つ可能性があるため、ここに残しておきます。

まず、FS(フィールドセパレーター)ではなく、RS(レコードセパレーター)が必要です。次に、リテラル*を渡すには、2回エスケープする必要があります。 1回は*をエスケープし、もう1回はバックスラッシュをエスケープします(それ以外の場合、awkは\rまたは\tと同じ方法で一致させようとします)。次に、2番目の「行」を印刷します。

$ awk -vRS='\\*\\*\\*' 'NR==2' file

thingsIwantToRead1   
thingsIwantToRead2   
thingsIwantToRead3  

出力の周りの空白行を回避するには、次を使用します。

$ awk -vRS='\n\\*\\*\\*\n' 'NR==2' file
thingsIwantToRead1   
thingsIwantToRead2   
thingsIwantToRead3  

これは、表示されている最初の段落の後だけでなく、each段落の後に***を想定していることに注意してください。

8
terdon

@terdonの回答に加えて、awk(およびsed)を使用すると、範囲パターンを使用できます。

awk '/sep1/,/sep2/{print}' file

または

sed -n '/sep1/,/sep2/p' file

すべてを含む(含む)sep1およびsep2。あれは:

~$ awk '/sep1/,/sep2/{print}' file
sep1
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3
sep2

あなたの場合:

~$ awk '/\*\*\*/,/^$/{print}' file
***
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3
 

次に、最初と最後の行を削除することをお勧めします。

たとえば:

~$ sed -n '/\*\*\*/,/^$/p' file | sed '1d;$d'
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

または

~$ awk '/\*\*\*/,/^$/{print}' file | awk 'NR>1&&!/^$/ {print}'
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

段落が長すぎない場合。

6
fredtantini

質問の編集に基づいて更新されたバージョン:

Perlの使用:

< inputfile Perl -0777 -pe 's/.*[*]+\n(.*) <np>\n.*/$1/s' > outputfile
  • < inputfileinputfileのコンテンツをPerlstdinにリダイレクトします
  • -0777:Perlに、1行ずつではなくファイル全体を一度にスラップさせる
  • -p:Perlに行を印刷させる
  • -e:Perlに引数からプログラムの行を読み取らせる
  • > outputfilePerlstdoutのコンテンツをoutputfileにリダイレクトします

正規表現の内訳

  • s:置換を実行するようにアサートします
  • /:検索パターンを開始します
  • .*[*]+\n:1つ以上の*文字で終わり、直後に改行文字が続く文字列の終わりまでの任意の数の文字に一致します
  • (.*) <np>:任意の数の任意の文字を任意の文字まで照合してグループ化し、その直後に<np>\n文字列を続けます
  • .*:任意の数の任意の文字に一致します
  • /:検索パターンを停止/置換パターンを開始
  • $1:キャプチャされたグループに置き換えられます
  • /:置換パターンを停止/修飾子を開始
  • s:入力文字列を単一行として扱うようにアサートし、.に改行文字も一致させる

サンプル出力:

~/tmp$ cat inputfile
13.2000000000     , 3*0.00000000000       ,  11.6500000000     , 3*0.00000000000       ,  17.8800000000

Blablabla

  SATELLITE EPHEMERIS
     ===================
Output frame: Mean of J2000

       Epoch                  A            E            I           RA           AofP          TA      Flight Ang
*****************************************************************************************************************
2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311
 <np>
 ----------------
 Predicted Orbit:
 ----------------

 Blablabla
~/tmp$ < inputfile Perl -0777 -pe 's/.*[*]+\n(.*) <np>\n.*/$1/s'
2012/10/01 00:00:00.000     6998.239     0.001233     97.95558     77.41733     89.98551    290.75808    359.93398
2012/10/01 00:05:00.000     6993.163     0.001168     97.95869     77.41920    124.72698    274.57362    359.93327
2012/10/01 00:10:00.000     6987.347     0.001004     97.96219     77.42327    170.94020    246.92395    359.94706
2012/10/01 00:15:00.000     6983.173     0.000893     97.96468     77.42930    224.76158    211.67042    359.97311
~/tmp$ 

元のバージョン:

Perlの使用:

< inputfile Perl -0777 -pe 's/.*[*]{3}\n(.*\n)\n.*/$1/s' > outputfile
  • < inputfileinputfileのコンテンツをPerlstdinにリダイレクトします
  • -0777:Perlに、1行ずつではなくファイル全体を一度にスラップさせる
  • -p:Perlに行を印刷させる
  • -e:Perlに引数からプログラムの行を読み取らせる
  • > outputfilePerlstdoutのコンテンツをoutputfileにリダイレクトします

正規表現の内訳

  • s:置換を実行するようにアサートします
  • /:検索パターンを開始します
  • .*[*]{3}\n***\n文字列の末尾までの任意の数の任意の文字に一致します
  • (.*\n)\n:改行文字の直後の改行文字までの任意の数の文字に一致し、グループ化します
  • .*:任意の数の任意の文字に一致します
  • /:検索パターンを停止/置換パターンを開始
  • $1:キャプチャされたグループに置き換えられます
  • /:置換パターンを停止/修飾子を開始
  • s:入力文字列を単一行として扱うようにアサートし、.も改行文字と一致するように強制します

サンプル出力:

~/tmp$ cat inputfile
blablabla
blablabla
***
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3

blablabla
blablabla
~/tmp$ < inputfile Perl -0777 -pe 's/.*[*]{3}\n(.*\n)\n.*/$1/s'
thingsIwantToRead1
thingsIwantToRead2
thingsIwantToRead3
~/tmp$ 
1
kos