1

私がやりたいことは:

find some_files -name '*.html' -exec sed -i "s/`cat old`/`cat new`/g" {} \;

古いもの新しいものに改行文字とスラッシュおよびその他の特殊文字が含まれているため、sedが正しく解析されません。

sedとコマンドtr、コマンドprintf'%q'を使用して改行文字をエスケープする方法について読みましたが、機能を完全に理解していないためか、これらを正しく機能させることができません。さらに、sedが機能するためにまだエスケープしなければならない特殊文字がわかりません。

4

1 に答える 1

1

正確に何をしたいかはわかりませんが、古いファイルに改行が含まれていると、問題が発生する可能性があります。これは、sedが各行にコマンドを適用することで機能するため、複数の行を明示的にロードしない限り、行を複数の行を表すパターンと一致させようとしても機能しないためです。

私の提案は、substituteコマンドを適用する前に、ファイル全体をsedの「バッファー」にロードすることです。次に、古いものと新しいものが正しくエスケープされていることを確認する必要があります。また、さらに混乱する可能性があるのは、古いファイル(パターン)のエスケープは、新しいファイル(置換)のエスケープとは異なる必要があるということです。

新しいファイルを「new.tmp」ファイルにエスケープすることから始めましょう。わかりやすくするために、「escape_new.sed」というsedスクリプトを作成します。

#!/bin/sed -f

# Commas used as separators
s,\\,\\\\,g
s,$,\\,g
s,[/&],\\&,g
$ a/

次にそれを実行します:sed -f escape_new.sed new > new.tmp

エスケープするために使用するコマンドは3つあります。

  1. バックスラッシュの前に別のバックスラッシュを付ける必要があります
  2. 改行の前にはバックスラッシュを付ける必要があります(これを行うには、行の終わりの前にバックスラッシュを追加します)。
  3. アンパサンドとスラッシュの前には円記号を付ける必要があります(置換テキストの&は実際には一致を含む演算子であるため、スラッシュと一致する場合はスラッシュが含まれ、アンパサンドと一致する場合はアンパサンドが含まれることに注意してください) 。
  4. 最後の行(「$」記号で参照)に、(「a」コマンドを使用して)スラッシュを追加します。これは、後で使用する代替コマンドの終了スラッシュです。バックティックは入力の最後にある余分な改行を削除し、問題を引き起こす可能性があるため、ここに配置する必要があります(たとえば、実際に終了スラッシュを引用する改行を引用するために使用されるバックスラッシュなど)。

それでは、古いファイルをエスケープしましょう。上記のように、「escape_old.sed」スクリプトを作成します。ただし、その前に、ファイル全体をパターンスペース(sedの内部バッファー)にロードして、改行文字を置き換える必要があります。次のコマンドでこれを行うことができます。

: a
$! {
    N
    b a
}

最初のコマンドは、「a」というラベルを作成します。2番目のコマンド( "{")は、実際にコマンドのグループを開始します。ここでの魔法は「$!」です。アドレスプレフィックス。そのプレフィックスは、読み取られた最後の入力行が入力の最後の行ではなかった場合にのみコマンドを実行するように指示します(「$」は入力の最後の行を意味し、「!」はそうではないことを意味します)。グループの最初のコマンドは、入力からパターンスペースに次の行を追加します。この「N」コマンドを最後の行で実行するとスクリプトが終了するため、最後の行で実行しないように注意する必要があります。グループの2番目のコマンドは分岐コマンド「b」で、「a」ラベルに「ジャンプ」して戻ります。魔法は「$!」です コマンドの前にあるアドレスプレフィックス。閉じ括弧はグループを閉じます。このグループは、それぞれのアドレスプレフィックスを使用して、すべての行をループして連結し、最後の行の後で停止して、さらにコマンドを実行できるようにします。次に、最終的なスクリプトがあります。

#!/bin/sed -f

: a
$! {
    N
    b a
}

s,\\,\\\\,g
s,\n,\\n,g
s,[][/^$.],\\&,g

上記のように、特殊文字をエスケープする必要があります。この場合、実際の改行はバックスラッシュとそれに続く文字nとしてエスケープされます。最後のコマンドには、バックスラッシュを前に付ける必要のある文字がさらにあります。閉じ角かっこを一致させるには、sedが一致する文字リストの終了文字として解釈しないように、角かっこ内の最初の文字である必要があることに注意してください。したがって、角括弧の間に順番にリストされている文字は][/^$.です。

また、次のように実行します。sed -f escape_new.sed old > old.tmp

これで、これらのエスケープされたファイルをsedコマンドで使用できますが、ここでもすべての行をパターンスペースにロードする必要があります。以前と同じコマンドを使用しますが、それらを1行に配置すると、コンパクトな形式になります:a;$!{N;ba}::これを最終式で使用できるようになりました(new.tmpファイルにあるスラッシュ文字なしで):

find some_files -name '*.html' -exec sed -e ":a;\$!{N;ba};s/`cat old.tmp`/`cat new.tmp`g" -i {} \;

そしてうまくいけばそれはうまくいくでしょう=)

バックスラッシュを使用してシンボルをエスケープしたことに注意してください$。エスケープしないと、シェルは変数にアクセスしようとしていると見なし $!ます(最後に実行された非同期コマンドの結果)。

于 2012-09-28T16:59:03.513 に答える