私は2つの特定の連続した文字列のすべてのインスタンスを置き換えるPerlの1行のコードを書くのが好きです。
john paul
たとえば、2つの文字列がおよびで、george
この文字列の連続したインスタンスをその順序で置き換えたいとしますpete
。 1行のコード実行
$ cat ~/foo
john paulgeorge
john paul george
john paul
george
george john paul
結果が出なければならない
$ cat ~/foo
pete
pete
pete
george john paul
私の心に浮かぶ唯一のこと
$ perl -p -i -e 's/john paul\s*george/pete/g' ~/foo
しかし、これによって
$ cat ~/foo
pete
pete
john paul
george
george john paul
私の回線の1つを変更する方法はありますか?
答え1
1行のコードに追加する唯一のことは、ファイルを単一の文字列として読み取るオプションです。
perl -0777 -p -i -e 's/john paul\s*george/pete/g' ~/foo
# ^^^^^
答え2
perl
-n
オプションは-p
プログラムの周りにバリアントを配置し、while (<>) { ... }
入力を1行ずつ処理します。複数行にわたって置き換えるには、内容全体を文字列として読み取る必要があります。これは直接行う必要があります。
perl -e 'local $/;$_=<>;s/john paul\s*george/pete/g;print'
これは定義されていません$/
、レコード区切り記号、する<>
喫煙行は分割されず、$_
入力全体が一度に読み取られ、長い文字列が置き換えられます。また、直接印刷する必要があります。
ここにはもう魔法はありません。これは完全なPerlプログラムを書くのに少し不便な方法です。-i
ただし、内部交換にも使用できます。
大きなファイルがある場合、これはかなり非効率的(またはメモリを使い果たす)になる可能性がありますが、より良いパーサーを構築しない限り、これはやや避けられないようです。他の選択肢を見てみるperldoc -q 'entire file'
と、多くの人が本気ではないと言うでしょう。
答え3
sed
ファイル全体を占有しなくてもこれを行うことができます。
sed -e ':top' -e 's/john paul[[:space:]]*george/pete/g;$b' -e '/john paul[[:space:]]*$/!b' -e 'N;btop' input
これはメモリ使用量がはるかに少なくなります。現在の行から始まり、複数の行を一致させる可能性がある場合にのみ、複数の行を吸収します。これにより、一致するものが見つかるまで、または一致する可能性がなくなるまで迷惑になります。
また、POSIXと互換性があります。 (PerlはPOSIXの一部ではありません。)コメントからこの点を指摘してくれたmikeservに感謝します。
説明する:
:top
というラベルを設定しますtop
。
s/john paul[[:space:]]*george/pete/g
パターンスペースのすべての項目を必要に応じて置き換えます。 (デフォルトはプログレッシブです。)
$b
現在の行がファイルの最後の行である場合は、最後に移動して印刷します。
/john paul[[:space:]]*$/!b
:
パターンはパターンスペースの末尾で一致し、/john paul[[:space:]]*$/
その後にスペースが数にかかわらず(ただし、スペースのみあり)、その後にパターンが反転されます。したがって、ここでの効果は、多重化の可能性がない場合にのみコマンドを実行することです(スクリプトの最後にジャンプしてパターンスペースを印刷し、ファイルから次の行を読み取り、スクリプトの先頭から始めます)。現在のパターン空間行の一致から始まります。john paul
!
b
N
ファイルの次の行をパターンスペースに追加します(改行文字を追加した後)。
btop
:top
パターンスペースを消去せずにラベルに分岐します。
答え4
ファイルを読み取るには、-0777オプションを使用する必要があります。ただし、\ sも\ nと一致するようにするには、最後にm修飾子を追加する必要があります。
Perlが-0を検出すると、入力レコード区切り文字($ /)は次のように更新されます。たとえば、-00 と入力すると、Perl は $/ を短絡モードに設定します。だから
perl -0777 -pe 's/^john paul\s*george/pete/gm' george.txt
以下と同じ:
perl -pe 'BEGIN { undef $/ ; } s/^john paul\s*george/pete/gm' george.txt