3番目のファイルでout.txtの内容をin.txtに置き換える方法は?

3番目のファイルでout.txtの内容をin.txtに置き換える方法は?

main.txtout.txtの3つのファイルがあります。 inin.txtのすべての内容をの内容に変えたいと思います。out.txtmain.txtin.txt

out.txt両方にin.txt複数の行とさまざまな特殊文字を含めることができます。この文字列を正しく読み、エスケープするにはどうすればよいですか?

これは、特殊文字、重複一致、不完全一致、冗長一致など、いくつかの極端なケースの例です。

main.txt:

foo
Replace these
three lines
with some $.*\'"& in it
bar
Replace these
three lines
with some $.*\'"& in it
Replace these
three lines
with some $.*\'"& in it

three lines
Replace these
three lines
three lines
with some $.*\'"& in it
baz

out.txt:

Replace these
three lines
with some $.*\'"& in it

in.txt:

Replacement lines
also with $.*\'"&

予想出力:

foo
Replacement lines
also with $.*\'"&
bar
Replacement lines
also with $.*\'"&
Replacement lines
also with $.*\'"&

three lines
Replace these
three lines
three lines
with some $.*\'"& in it
baz

答え1

そしてperl

perl -0777 -e '$out = <>; $in = <>; $_ = <>; s/\Q$out\E/$in/g; print
              ' out.txt in.txt main.txt > new-main.txt

メモリに収まるほど小さい限り、ファイルに含めることができるすべての文字または非文字で動作する必要があります(バイナリファイルでも機能します)。

-0777入力レコード区切り文字を不可能な値に設定して実行と同じにし、引数として渡されたファイル$/ = undefから<>順番にファイル全体を読み取ります。

したがって、//全体の内容が、および、$outそれぞれあります。$in$_out.txtin.txtmain.txt

$_s/pattern/replacement/flagsprint基本的に演算子によって処理される変数です。パターン空間存在するsed

ここでのパターンは、内部コンテンツが正規表現ではなく文字通りに処理されるようにすることです\Q$out\E\Q...\Eこのgフラグは、のように発生するすべての状況を置き換えますsed


または、コマンド出力(たとえばls|<<>>ファイルパスとしてのみ解釈されるコマンド出力)を代わりに使用します。

答え2

すべてのUnixシステムのすべてのシェルでawkを使用してください。

$ cat tst.awk
FILENAME == ARGV[1] { old = old $0 ORS }
FILENAME == ARGV[2] { new = new $0 ORS }
FILENAME == ARGV[3] { rec = rec $0 ORS }
END {
    lgth = length(old)
    if ( lgth > 0 ) {
        while ( beg = index(rec,old) ) {
            printf "%s%s", substr(rec,1,beg-1), new
            rec = substr(rec,beg+lgth)
        }
    }
    printf "%s", rec
}

$ awk -f tst.awk out.txt in.txt main.txt
foo
Replacement lines
also with $.*\'"&
bar
Replacement lines
also with $.*\'"&
Replacement lines
also with $.*\'"&

three lines
Replace these
three lines
three lines
with some $.*\'"& in it
baz

上記はリテラル文字列のマッチングと置換を行うため、入力ファイル内のすべての文字に対して機能します。

答え3

シェルがそれをサポートしている場合<()(たとえばzsh、、、、、kshなどbash)、ファイル間にマーカーを挿入して(ここでMARK)、ファイルを区切ってPOSIXを使用できますsed

sed -e 'H;1h;$!d;x;:L
        s/^\(.*\)\(MARK\n\)\(.*\)\2\(.*\)\1/\1\2\3\2\4\3/;tL
        s/.*MARK\n//' out.txt <(echo MARK) in.txt <(echo MARK) main.txt
  • H;1h;$!d;xファイル全体を一度に処理する一般的なモードです。
  • :Lサイクルを始める
  • s/^\(.*\)\(MARK\n\)\(.*\)\2\(.*\)\1/\1\2\3\2\4\3/out.txt次へ交換in.txt
  • tL交換できればリサイクルできます。
  • s/.*MARK\n//印刷する前に他のファイルを削除してください。

制限事項に注意してください。

  1. MARK当然、テキストの一部ではないセクションを選択する必要があります。
  2. 説明したように、実装によっては大容量ファイルの場合は失敗する可能性がありますsed。私は現代のシステムでこの境界に遭遇したことはありませんが、存在します。一致しない行を更新すると問題は解決しますが、これは最初からプログラミングにすぎず、目的ではありませんsed

関連情報