正規表現に一致する最初の行の後のファイルの部分を取得するにはどうすればよいですか?


169

約1000行のファイルがあります。私のgrepステートメントに一致する行の後のファイルの部分が必要です。

あれは:

$ cat file | grep 'TERMINATE'     # It is found on line 534

したがって、535行目から1000行目までのファイルをさらに処理する必要があります。

どうやってやるの?


34
UUOC(猫の無用な使用):grep 'TERMINATE' file
ジェイコブ

30
私はそれを私がそのように使用するようなものであることを知っています。質問に戻りましょう。
Yugal Jindle、2011

3
これは完全に細かいプログラミングの質問であり、stackoverflowに適しています。
aioobe 2011

13
@Jacob猫を無駄に使うことは決してありません。その使用は、我々が使用できることを意味し、標準出力にファイル、印刷することであるgrepというに適用するために切り替えるものを学ぶことよりも、中のデータを読み取るための標準入力インターフェイスをgrep、そしてsed、そしてawk、そしてpandoc、そしてffmpeg、我々が読みたいときなどファイルから。同じことをするたびに新しいスイッチを学習する必要がないため、時間を節約できます。つまり、ファイルから読み取ります。
runeks

@runeks私はあなたの感情に同意します-しかし猫なしでそれを達成することができます:grep 'TERMINATE' < file。多分それは読みを少し難しくします-しかしこれはシェルスクリプトなので、それは常に問題になるでしょう:)
LOAS

回答:


307

以下はTERMINATE、ファイルの最後まで一致する行を出力します。

sed -n -e '/TERMINATE/,$p'

説明: -nの無効のデフォルト動作sedそれにそのスクリプトを実行した後、各ラインを印刷するの、-eとスクリプトを示しsed/TERMINATE/,$一致する最初の行を意味アドレス(ライン)の範囲の選択ですTERMINATE(ファイルの末尾に(grepのような)正規表現を$) 、およびp現在の行を印刷する印刷コマンドです。

これは、一致する行に続く行 からTERMINATEファイルの最後まで印刷します
(一致する行の後から一致する行を除く、一致する行の後)。

sed -e '1,/TERMINATE/d'

説明: 正規表現に1,/TERMINATE/一致する最初の行への入力の最初の行を意味するアドレス(行)範囲選択であり、現在の行を削除して次の行にスキップする削除コマンドです。デフォルトの動作は、ラインを印刷することで、それが後の行を出力します 入力の最後に。TERMINATEdsedTERMINATE

編集:

前の行が必要な場合TERMINATE

sed -e '/TERMINATE/,$d'

またTERMINATE、1つのパスで2つの異なるファイルの前後の両方の行が必要な場合:

sed -e '1,/TERMINATE/w before
/TERMINATE/,$w after' file

beforeおよびafterファイルには、terminateを含む行が含まれるため、それぞれを処理するには、次のように使用する必要があります。

head -n -1 before
tail -n +2 after

Edit2:

sedスクリプトでファイル名をハードコーディングしたくない場合は、次のことができます。

before=before.txt
after=after.txt
sed -e "1,/TERMINATE/w $before
/TERMINATE/,\$w $after" file

ただし$、最後の行の意味をエスケープして、シェルが$w変数を展開しようとしないようにする必要があります(スクリプトの前後に一重引用符ではなく二重引用符を使用していることに注意してください)。

sedがファイル名の終わりを知るために、スクリプトのファイル名の後に新しい行が重要であることを伝えるのを忘れていました。


編集: 2016-0530

セバスチャンクレメントは、「ハードコードTERMINATEされた変数をどのように変数で置き換えますか?」

一致するテキストの変数を作成し、前の例と同じ方法で行います。

matchtext=TERMINATE
before=before.txt
after=after.txt
sed -e "1,/$matchtext/w $before
/$matchtext/,\$w $after" file

前の例で一致するテキストに変数を使用するには:

## Print the line containing the matching text, till the end of the file:
## (from the matching line to EOF, including the matching line)
matchtext=TERMINATE
sed -n -e "/$matchtext/,\$p"
## Print from the line that follows the line containing the 
## matching text, till the end of the file:
## (from AFTER the matching line to EOF, NOT including the matching line)
matchtext=TERMINATE
sed -e "1,/$matchtext/d"
## Print all the lines before the line containing the matching text:
## (from line-1 to BEFORE the matching line, NOT including the matching line)
matchtext=TERMINATE
sed -e "/$matchtext/,\$d"

これらの場合にテキストを変数に置き換えることに関する重要なポイントは次のとおりです。

  1. 変数($variablename)で囲まれたsingle quotes[ ']「を展開」しませんが、変数内のdouble quotes[ "]になります。だから、あなたはすべて変更する必要single quotesdouble quotes変数に置き換えるテキストが含まれ場合は、する必要があります。
  2. sed範囲も含まれてい$て、すぐのような文字が続いています:$p$d$w。あなたはこれらのエスケープする必要がありますので彼らはまた、拡大する変数のようになります。$バックスラッシュで文字を[ \よう]: 、\$p、。\$d\$w

どうすればTERMINATEの前に行を取得し、それに続くすべてを削除できますか?
Yugal Jindle、2011

ハードコードされたTERMINALを変数でどのように置き換えますか?
セバスチャンクレメント2016

2
ここで欠落している1つの使用例は、最後のマーカーの後に行を印刷する方法です(ファイルに複数の行がある場合は、ログファイルなどと考えてください)。
mato 2016年

この例sed -e "1,/$matchtext/d"$matchtext、最初の行にある場合は機能しません。に変更する必要がありましたsed -e "0,/$matchtext/d"
Karalga 2017年

61

単純な近似として、次のように使用できます

grep -A100000 TERMINATE file

何のために TERMINATE、その行に続いて最大100000行出力します。

マニュアルページから

-A NUM, --after-context=NUM

一致する行の後に、NUM行の後続コンテキストを出力します。 一致する隣接するグループの間に、グループセパレーター(-)を含む行を配置します。-oまたは--only-matchingオプションを使用すると、これは効果がなく、警告が表示されます。


これでうまくいくかもしれませんが、多くのファイルを処理するには、それをスクリプトにコーディングする必要があります。だから、いくつかの一般的な解決策を示してください。
Yugal Jindle、2011

3
これは実用的な解決策の1つだと思います。
ミシェルゴッタ2013

2
同様に、-B NUM、-before-context = NUM​​行を一致させる前に、先行コンテキストのNUM行を出力します。一致する隣接するグループの間に、グループセパレーター(-)を含む行を配置します。-oまたは--only-matchingオプションを使用すると、これは効果がなく、警告が表示されます。
PiyusG 2014

チェックする文字列として変数を簡単に使用できるため、この解決策がうまくいきました。
Jose Martinez

3
良いアイデア!あなたは、コンテキストのサイズがわからない場合は、行をカウントすることができるfile代わりに、:grep -A$(cat file | wc -l) TERMINATE file
レミング

26

ここで使用するツールはawkです。

cat file | awk 'BEGIN{ found=0} /TERMINATE/{found=1}  {if (found) print }'

これはどのように作動しますか:

  1. 変数「found」をゼロに設定し、falseを評価します
  2. 正規表現で「TERMINATE」の一致が見つかった場合は、1に設定します。
  3. 「見つかった」変数がTrueと評価された場合は、出力します:)

他のソリューションは、非常に大きなファイルで使用すると、大量のメモリを消費する可能性があります。


シンプルでエレガント、そして非常に汎用的です。私の場合、 '###'が2回出現するまですべてを印刷していました:cat file | awk 'BEGIN{ found=0} /###/{found=found+1} {if (found<2) print }'
Aleksander Stelmaczonek '16

3
ここで使用しないツールはcatです。awkは、引数として1つ以上のファイル名を取ることが完全に可能です。stackoverflow.com/questions/11710552/useless-use-of-cat
tripleee

9

私が正しくあなたの質問を理解していれば、あなたはラインたいです後に TERMINATE含めない、TERMINATE-lineを。awk簡単な方法でこれを行うことができます:

awk '{if(found) print} /TERMINATE/{found=1}' your_file

説明:

  1. ベストプラクティスではありませんが、すべての変数のデフォルトは0であるか、定義されていない場合は空の文字列であるという事実に頼ることができます。したがって、最初の式(if(found) print)は、最初から何も出力しません。
  2. 印刷が完了したら、これがスターターラインであるかどうかを確認します(これは含めないでください)。

これにより、-lineの後にすべての行が印刷されますTERMINATE


汎化:

  • あなたが持っているファイルの開始 -およびエンド -linesを、あなたはそれらの行の間の線たい除く開始を -とエンド -lines。
  • 開始行終了行は、行に一致する正規表現で定義できます。

例:

$ cat ex_file.txt 
not this line
second line
START
A good line to include
And this line
Yep
END
Nope more
...
never ever
$ awk '/END/{found=0} {if(found) print} /START/{found=1}' ex_file.txt 
A good line to include
And this line
Yep
$

説明:

  1. end -lineが見つかった場合、印刷は行われません。このチェックは、最後を除外するために実際の印刷のに行われることに注意してください、結果から -line。
  2. foundが設定されている場合、現在の行を印刷します。
  3. start -lineが見つかった場合はfound=1、次の行が出力されるように設定します。このチェックは実際の印刷に行われ、結果から開始行が除外されることに注意してください。

ノート:

  • このコードは、すべてのawk-varsのデフォルトが0であるか、定義されていない場合は空の文字列であるかに依存しています。これは有効ですが、ベストプラクティスではない可能性があるため、BEGIN{found=0}、awk式の先頭にを。
  • 複数の開始-終了ブロックが見つかった場合、それらはすべて出力されます。

1
素晴らしい素晴らしい例。2時間かけてcsplit、sed、およびあらゆる種類の複雑なawkコマンドを確認しました。これは私が望んだことをしただけでなく、必要な他のいくつかの関連することを行うためにそれを変更する方法を推測するのに十分単純なものでした。awkが素晴らしくて、判読不能ながらくたの混乱だけではないことを思い出させます。ありがとう。
user1169420

{if(found) print}awkのアンチパターンのビットです。ブロックを単に置き換えるfoundfound;、後で別のフィルターが必要になった場合に置き換えるのがより慣用的です。
user000001

@ user000001説明してください。何をどのように置き換えるかわかりません。とにかく、私はその書かれた方法が何が起こっているのか非常に明確にすると思います。
UlfR

1
あなたは代わるawk '{if(found) print} /TERMINATE/{found=1}' your_fileawk 'found; /TERMINATE/{found=1}' your_file、彼らは両方とも同じことを行う必要があり、。
user000001

7

次のようなbashパラメータ展開を使用します。

content=$(cat file)
echo "${content#*TERMINATE}"

何してるの?
Yugal Jindle、2011

「ファイル」のコンテンツを$ content変数にコピーしました。その後、「TERMINATE」が表示されるまで、すべてのキャラクターを削除しました。貪欲マッチングは使用していませんが、$ {content ## * TERMINATE}で貪欲マッチングを使用できます。
Mu Qiao

ここでは、bashのマニュアルのリンクは次のとおりです。gnu.org/software/bash/manual/...
ムー橋

6
ファイルのサイズが100GBの場合はどうなりますか?
Znik

1
反対票:これは恐ろしい(ファイルを変数に読み込む)と間違っています(引用符を付けずに変数を使用します。適切に使用するprintfか、渡されるものを正確に知っている必要がありechoます)。
Tripleee

6

grep -A 10000000「TERMINATE」ファイル

  • sedよりもはるかに高速で、特に非常に大きなファイルで作業します。最大1000万行(またはユーザーが入力したもの)まで機能するため、これをヒットしたものを処理するのに十分な大きさにしても害はありません。

4

sedまたはでそれを行うには多くの方法がありますawk

sed -n '/TERMINATE/,$p' file

これはTERMINATEファイルを検索し、その行からファイルの最後まで印刷します。

awk '/TERMINATE/,0' file

これは、とまったく同じ動作sedです。

印刷を開始する行の番号がわかっている場合は、(行のNR番号を最終的に示すレコードの数)と一緒に指定できます。

awk 'NR>=535' file

$ seq 10 > a        #generate a file with one number per line, from 1 to 10
$ sed -n '/7/,$p' a
7
8
9
10
$ awk '/7/,0' a
7
8
9
10
$ awk 'NR>=7' a
7
8
9
10

あなたが使用できる数more +7 file
123

これには一致する行が含まれますが、これはこの質問で必要なものではありません。
mivk

@mivkよく、これは受け入れられた回答の場合にも当てはまり、2番目に賛成されているため、問題は誤解を招くタイトルである可能性があります。
fedorqui 'SO stop harming' 2016

3

なんらかの理由でsedの使用を避けたい場合、次のコードTERMINATEはファイルの最後まで一致する行を出力します。

tail -n "+$(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)" file

次の行は、次の行のマッチングTERMINATEからファイルの最後まで印刷します。

tail -n "+$(($(grep -n 'TERMINATE' file | head -n 1 | cut -d ":" -f 1)+1))" file

1つのプロセスでsedが実行できることを実行するには2つのプロセスが必要です。grepとtailの実行の間にファイルが変更されると、結果が一貫しなくなる可能性があるため、sedの使用をお勧めします。さらに、ファイルにが含まれていない場合TERMINATE、最初のコマンドは失敗します。


ファイルは2回スキャンされます。100GBサイズの場合はどうなりますか?
Znik

1
これはくだらないソリューションなので反対票を投じますが、回答の90%が警告であるため反対票を投じます。
Mad Physicist


0

これは、1つの方法である可能性があります。ファイルのどの行にgrepワードがあり、ファイルに何行あるかがわかっている場合:

grep -A466 'TERMINATE'ファイル


1
行番号がわかっている場合はgrep必要ありません。だけを使用できるtail -n $NUMので、これは実際の答えではありません。
Samveen

-1

sedは、ジョブにとってはるかに優れたツールです:sed -n '/ re /、$ p' file

ここで、reは正規表現です。

別のオプションは、grepの--after-contextフラグです。終了する数値を渡す必要があります。ファイルでwcを使用すると、終了する正しい値が得られるはずです。これを-nおよび一致表現と組み合わせます。


--after-contextは問題ありませんが、すべての場合ではありません。
Yugal Jindle、2011

他に何か提案できますか?
Yugal Jindle、2011

-2

これらは、最後に見つかった行「TERMINATE」からファイルの終わりまでのすべての行を印刷します。

LINE_NUMBER=`grep -o -n TERMINATE $OSCAM_LOG|tail -n 1|sed "s/:/ \\'/g"|awk -F" " '{print $1}'`
tail -n +$LINE_NUMBER $YOUR_FILE_NAME

でライン番号を抽出してgrepフィードできるようにするのtailは、無駄なアンチパターンです。一致を見つけてファイルの最後まで印刷する(または、逆に、最初の一致で印刷して停止する)は、通常の不可欠な正規表現ツール自体で主に行われます。大規模なものgrep | tail | sed | awkは、それ自体、および友人の大規模な無用な使用でもgrepあります
tripleee 2016

私は彼が「TERMINATE」の/ last instance /を見つけ、そのインスタンスからの行を与えるような何かを私たちに与えようとしていたと思います。他の実装では、最初のインスタンス以降を提供します。LINE_NUMBERはおそらく次のようになります:LINE_NUMBER = $(grep -o -n 'TERMINATE' $ OSCAM_LOG | tail -n 1 | awk -F: '{print $ 1}')おそらく最もエレガントな方法ではありませんが、それは仕事を成し遂げたようです。^。^
fbicknel

...またはすべて1行であるが醜い:tail -n + $(grep -o -n 'TERMINATE' $ YOUR_FILE_NAME | tail -n 1 | awk -F: '{print $ 1}')$ YOUR_FILE_NAME
fbicknel

.... $ YOUR_FILE_NAMEの代わりに戻って$ OSCAM_LOGを編集しようとしていましたが、何らかの理由でできません。$ OSCAM_LOGの出所はわかりません。私はそれを無頓着にオウムした。オブジェクト指向
fbicknel

これをAwkだけで行うことは、Awk 101の一般的なタスクです。行番号を取得するためだけにすでにより優れたツールを使用している場合は、手放しtailて、より優れたツールでタスクを実行してください。とにかく、タイトルは明らかに「最初の試合」と言っています。
Tripleee 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.