main.txt
、out.txt
の3つのファイルがあります。 inin.txt
のすべての内容をの内容に変えたいと思います。out.txt
main.txt
in.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.txt
in.txt
main.txt
$_
s/pattern/replacement/flags
print
基本的に演算子によって処理される変数です。パターン空間存在する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//
印刷する前に他のファイルを削除してください。
制限事項に注意してください。
MARK
当然、テキストの一部ではないセクションを選択する必要があります。- 説明したように、実装によっては大容量ファイルの場合は失敗する可能性があります
sed
。私は現代のシステムでこの境界に遭遇したことはありませんが、存在します。一致しない行を更新すると問題は解決しますが、これは最初からプログラミングにすぎず、目的ではありませんsed
。