grepパターンを一致させた後にファイル全体を編集するには?

grepパターンを一致させた後にファイル全体を編集するには?

簡単にするためにパターンを一致させた後、ファイル全体を編集したいと思います。ファイルの例は次のとおりです。

$ cat file
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=x.x.x.x
blahblah
mask=255.0.0.0
host=d

ホストcのIPを編集したいのですが、変数がある可能性があるので値がわからないとしましょう。その行を印刷するgrep host cために使用すると、元の-B2ファイルで編集することはできません!別の点は、行がホストdの場合と同じ構造を持たない可能性があることです。ipmask情報の間にいくつかのテキストがあるため、IPパターンが常に私の検索パターンより2行前にあるとは限りません。

まだIPがわからないので、直接grepすることはできませんが、代わりにホストを検索してこの一致の前に行を編集してIP値を変更する必要があります。どうすればいいですか?

答え1

ファイルを後ろに移動すると、はるかに簡単になります。幸いなことに、これは簡単に行うことができますtac(リバースタスクですcat)。その後、比較的簡単なawkスクリプトを使用して目的のスクリプトを見つけてhost変更できますip

$ tac input | awk -v OFS="=" -v myip="changed_address" -v myhost="d" -F"=" '$1 == "host" { if( $2 == myhost ) { sw = "on" } else { sw="off" } } sw == "on" && $1 == "ip" { $2=myip } { print $0 }' | tac
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=changed_address
blahblah
mask=255.0.0.0
host=d

awkどのように機能するかを詳しく説明します。

まず、いくつかの変数を宣言します。各変数はforhostで、新しい値は次のとおりですip

-v myip="changed_address" -v myhost="d"

また、入力と出力のフィールド区切り文字を宣言します。

-v OFS="=" -F"="

実際のawkスクリプト自体は次のようになります。

$1 == "host" {             // If we see the "host" line..
  if( $2 == myhost ) {     // And it matches the one we're looking for..
    sw = "on"              // Set a flag to swap the next IP
  } else { 
    sw="off"               // Otherwise, unset the flag
  }
} 

sw == "on" && $1 == "ip" { // If the flag is set and this is an IP line.. 
    $2=myip                // Swap in the new IP 
}

{
   print $0                // Finally, print out the processed line
}

すべての操作が完了したら、もう一度使用して元にtac戻して前に戻ります。

答え2

これにより、ホストに関連付けられているIPがc次に変わります1.2.3.4

$ sed 's/^ip/\nip/' file | perl -00pe 'if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/} s/\n\n/\n/' 
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=1.2.3.4
mask=255.0.0.0
host=c
ip=x.x.x.x
blahblah
mask=255.0.0.0
host=d

説明する:

  • sed 's/^ip/\nip/' file\n:で始まる各行に追加の改行文字()を追加しますip。私はこれがすべての実装でうまくいかないと思うのでsed、あなたの実装がこれをサポートしていない場合は、sedコマンドをに置き換えてくださいperl -pe 's/^ip/\nip/'。 Perlの「短絡モード」(下記参照)を使用するにはこれが必要です。

  • perl -00pe-00Perlが「ショートモード」で実行されるようにします。ここで、「行」は2つの連続した改行として定義される。これにより、各ホストのブロックを「ライン」として扱うことができます。-pe「与えられたスクリプトを適用した後、各行を印刷します」を意味します-e

  • if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/}:この「行」(部分)が改行文字と一致し、その後に文字列が来てからhost=c別の改行文字が来る場合、ip=これを空白ではない文字()1つ以上に置き換えます。\S+ip=1.2.3.4

  • s/\n\n/\n/各改行文字のペアを単一の改行文字に置き換えて、元のファイルの形式を復元します。

ファイルを変更するには、次を使用できます。

tmp=$(mktemp); sed 's/^ip/\nip/' file > $tmp; 
perl -00pe 'if(/\nhost=c\n/){s/ip=\S+/ip=1.2.3.4/} s/\n\n/\n/' $tmp > file

答え3

別のオプションは、次のアドレスにいくつかのコマンドを送信することですed

h=c
ed -s file <<< $'/^host='"${h}"$'$\n?^ip=\nc\nip=new.ip.here\n.\nw\nq' > /dev/null

デフォルトのアイデアは、改行で区切られたコマンドのリスト(2つのANSI C引用符付き文字列)でファイルとパイプを編集し、指定されたホスト$' ... '(変数から$hip=を検索し、Make it newで始まる行を逆方向に検索して保存します。です。そして終了しますed。改行( )で区切られた\nコマンドは次のとおりです。

  1. /^host=... $h $- ()行で始まる文字列検索を開始し、()/内容を検索し、最後に()行の終わりを検索します。ホストが正確に一致しない場合は、行末の要件を軽減してください。host=^$h$

  2. ?^ip=- これで一致する行にあるので、ip=行の先頭にあるテキストを逆に検索します。

  3. c- この行を変更してください

  4. テキストの挿入ip=new.ip.here

  5. .- 挿入されたテキストの終わり

  6. w--ディスクにファイルを書き込む

  7. q——編集終了

-sファイルを開いて保存するときに放出されるバイト数を無音で呼び出してedを呼び出します。検索中の行が見つかったら、コマンド全体をリダイレクトして/dev/nullデフォルト出力を無音に設定します。edhost=ip=

答え4

$ awk -v host=c -v newip=zzz.zzz.zzz.zzz '$0 ~ "^host=" host "$" { print; getline; $0 = sprintf("ip=%s\s", newip) }; 1' file
ip=x.x.x.a
mask=255.0.0.0
host=a
ip=x.x.x.b
mask=255.0.0.0
host=b
ip=x.x.x.c
mask=255.0.0.0
host=c
ip=zzz.zzz.zzz.zzzs
blahblah
mask=255.0.0.0
host=d

これは、特定のホストのIPアドレスを変更しようとしており、IPアドレス行が常にhost=そのホストの行の後に表示されると想定しています。

プログラムはawk2つの変数とを設定して、コマンドラインからホスト名と新しいIPアドレスを取得します。その後、コードは指定されたホスト名に対応する行を見つけ、次の行(該当する行)を読み取り、削除し、新しいIPアドレスを使用して新しい行を作成します。データ(修正または変更されていない)はプログラムの尾によって出力されます。awkhostnewiphost=ip=ip=1

関連情報