web-dev-qa-db-ja.com

複数行の正規表現の一致後に行を挿入する

特定のタグセットの後に追加する必要のあるXMLファイルがあります。基本的に、ファイル構造は次のようになります。

<key>servers</key>
<dict>
... Server Details ...
</dict>

私は次の行で一致させたい:

<key>servers</key>
<dict>

<dict>の直後にサーバー接続を指定するXMLのブロックを追加します。かなり長いので、独自のテキストファイルに新しいXMLブロックがあります。

これを行うためにsedawkを見てきましたが、壁にぶつかりました。 Perlを使用してこれを実現する例も見ましたが、Perlについてはあまり詳しくありません。しかし、私が理解していることから、sedawkは複数行のマッチングが得意ではありません。

複数行の照合を行う必要がある理由は、<dict>タグがXMLファイル内で頻繁に使用され、コンテンツ全体を置き換えるのではなく、<key>servers</key>セクションにブロックを追加する必要があるためです。

5
user80235

次のようなことができます。

awk '{print}
     $0 == "<dict>" && previous == "<key>servers</key>" {
       system("cat other-file.xml")
     }
     {previous = $0}'
3
sed '/keys_line_1/,/keys_line_last/{/keys_line_last/{
h;s/unique_split_point.*//;r /path/to/insert/file
x;s/.*unique_split_point//;G
}}'

sedは、仮説の調整を要求することになると、正確に寛容ではありません。 sedが行うことはすべて、それが行ったばかりのことの直接の結果であるため、非常に小さなエラーが詳細にあると、結果が大幅に変わる可能性があります。

このように、少しの狂気と多くの忍耐がなければ、sedスクリプトのデバッグは苛立たしいことになる可能性がありますが、これらの品質を考えると、見返りは重要です。 I highly試行した場合と同じように、プロセス中にlookコマンドを使用することをお勧めします。

sed ...;l;...complicated_script...;l;...

実は、あなたの質問をもう一度見てみると、最初に思っていたよりも簡単だと思います。本当に必要なのは(私は思う)パターンスペースをサーバーマッチケースで1行拡張することだけです。デフォルトでは、sedはサイクルごとに1行のみをバッファリングしますが、コマンドNP、およびDはこの動作を直接制御します。

実際、私は元々この質問を誤解していたと思います。テキストを行のブロックではなく行に挿入したかったのですが。

だからあなたはたぶん必要です:

sed '\|<key>servers</key>|,/<dict>/N;P
/\n<dict>/!D;s/.*\n//;r /path/to/file'

そうすれば、関心のある行を除くすべての行serverからdictの一致までの範囲が、編集バッファーからできるだけ早く削除され、 * serverの任意のシーケンスに続く最初のdict一致に続いて、ターゲットファイルをrだけ出力し、fileが追加される唯一のポイント\newlineが前に付いている場合、出力バッファーはdict一致の直後に続きます。

3
mikeserv

このようなものは機能しますか?

sed '\|<key>servers</key>|{n
\|<dict>| r other-file.xml
}' file.xml
1
steeldriver

期待する結果についてもう少し具体的に教えてください。私はpython3と

PATH = '/My/Path/'

FILE = 'MyFile.xml'

for i, line in enumerate(open(PATH+FILE, 'r')):

... # 

\nをキャッチするのは簡単で、行は1で終わるので、探している行を見つけるのは簡単です。

しかし、続行するには結果をよりよく理解する必要があります

0
aVis

Perlタグを追加したので:

Perl -pE 'BEGIN{
              $/ = "<key>servers</key>\n<dict>\n"; 
              $content = `cat file.xml`
          }
          $_.=$content' your_input_file
0
Joseph R.