文字列パターンの最短一致を置き換える

文字列パターンの最短一致を置き換える

私はこの文字列を持っています:

update mytable set mycol=myvalue where mycol=yourvalue;

次に変換する必要があります。

insert into mytemp select * from mytable where mycol=youvalue;

私はこれを行うことができ、見事に動作します。

sed -e 's/^Update.*where//ig' -e "s/^/insert into mytemp select * from mytable where  /g" n.txt

しかし、:

文字列が次の場合:

update mytable set mycol=myvalue where mycol=(select yourcol from yourtable where youcol=yourvalue);

私は得る:

insert into mytemp select * from mytable where youcol=yourvalue);

そして私はほしい:

insert into mytemp select * from mytable where mycol=(select yourcol from yourtable where youcol=yourvalue);

どうですか?

答え1

基本的に、sed正規表現エンジンは貪欲です。これは、パターンが常に可能な最長の一致と一致することを意味します。貪欲ではない検索をする必要がありますが、sedは貪欲な検索をサポートしていないようです。したがってsed、可能な限り短い一致を見つけるために、検索パターンにピボットポイントを追加する必要があります。

次の行は、貪欲ではない特殊なwマッチングをシミュレートしようとします。updatewhere

sed -e 's/^Update[^w]*where//ig'\
    -e "s/^/insert into mytemp select * from mytable where  /g" n.txt

perlおよびいずれかのような他の正規表現エンジンはこの機能をサポートしていますawk

しかし、あなたの場合は、次のような表現が必要だと思います。

sed -e 's/^Update.\+where\(.\+where.*\)$/\
insert into mytemp select * from mytable where \1/ig'  n.txt

あなたの特定の質問にもっと便利になります。

(上記のラインの予告編は、\ラインをより明確にするために追加されました。)

答え2

正規表現の一致は左から右に進み、最も長い一致に優先順位を付けます。したがって、その行^Update.*whereの最後の項目と一致します。where

このマッチングを実行する1つの方法は、non-greedy数量子を使用することです*。 Sedは非greedy数量子をサポートしていませんが、Perlはサポートしています。

perl -pe 's/^update.*?where//i; s/^/insert into mytemp select .*? from mytable where /'

データと一致する場合と一致しない可能性がある別のアプローチは、テーブル名と列設定で括弧を拒否することです。

sed -e 's/^update[^()]*where//i' -e 's/^/insert into mytemp select [^()]* from mytable where /'

より洗練されたアプローチは、最初のwhere最初のトークンを一意のトークンに置き換えてから置換を実行し、最後にトークンをに復元することですwhere。 sed は 1 行ずつ実行されるため、\n次のように行に改行文字が含まれないことが保証されます。 sed。

sed -e 's/ where /\n/' \
    -e 's/^update.*$//i' -e 's/^/insert into mytemp select .* from mytable where /' \
    -e 's/\n/ where/'

関連情報