sedで正規表現を使用する

sedで正規表現を使用する

これは私が理解できない一般的なトピックの具体的な例です。

長年にわたり、私は次のように正規表現とsedを使用して、ディレクトリ内のすべてのファイルから文字列内のすべてのエントリを再帰的に見つけて置き換えました。

#FIND $GLOBALS['timechecks'] and REPLACE with completely_different_string
shopt -s globstar dotglob;
for file in /var/www/**/*; do
  if [[ -f $file ]] && [[ -w $file ]]; then
    sed -i -- 's/\$GLOBALS\['\''timechecks'\''\]/completely_different_string/g' "$file"
  fi
done

問題は、bashで正規表現を使用する際に私が知らない間に逃した基本的なものがあることです。したがって、具体的な例には解決策がありません。

目標文字列に閉じ込められました。

$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));

私が思いついた正規表現はうまくいきません。

これは私のスクリプトのsed行と私が思いついた検索正規表現ですが、役に立ちません。

\$GLOBALS\['\''timechecks'\''\]=addTimeCheck_sparky[(]$GLOBALS\['\''timechecks'\''\][,][ ]number_format[(]microtime[(]true[)][,]6[,]'\''\.'\''[,]'\'''\''[)][,][ ]__LINE__[],[ ]basename[(]__FILE__[)][)][;]

正規表現デバッガ

この例では、正規表現デバッガを使用していますが、正規表現は私のターゲット文字列を見つけましたが、うまくいきませんでした。デバッガは次の場所にあります。このリンク。これは私のターゲット文字列を見つけるために表示される正規表現です。

\$GLOBALS\['timechecks\'\]=addTimeCheck_sparky\(\$GLOBALS\[\'timechecks\'\], number_format\(microtime\(true\),6,\'\.\',''\), __LINE__, basename\(__FILE__\)\)

正規表現デバッガ出力の問題:

まず、正規表現を試しました。

  1. そこで実行すると、デバッガの正規表現が機能する理由がわかりませんが、私のbashスクリプトでは機能しません。
  2. この正規表現は、bashとsedで学んだ正規表現と比較して「間違っている」ようです。
  3. これを行うスクリプトにデバッガの正規表現を挿入しても機能しません。
  4. 理解できないから直すことができません。

基本的な問題は、bash / sedで動作するようにデバッガの有効な正規表現を変換することについて何も知らないことです。

「bashでsedで正規表現を使用する方法」を検索しましたが、これが潜在的な問題であるという事実の説明が見つかりませんでした。

関連質問:ターゲット文字列を入力として受け入れ、それを見つけるための正規表現を提供するジェネレータがないのはなぜですか?

ベストアンサー1

\$GLOBALS\['\''timechecks'\''\]=addTimeCheck_sparky[(]$GLOBALS
                                                      ^

そこにはまだ脱出していない男がいた$

\['\''timechecks'\''\][,][ ]number_format[(]microtime[(]true[)]
[,]6[,]'\''\.'\''[,]'\'''\''[)][,][ ]__LINE__[],[ ]basename[(]__FILE__[)][)][;]
                                              ^^

おそらくそうする必要があります[,]

実際にエスケープしないことは$重要ではありません(少なくともGNU sedの場合)。ただし、これは[],[ ]内部にスペースがある角括弧式です[],。しかし、これは有効な正規表現なので、望むものではないため、エラーは発生しません。

ところで、実際に引用するというのは本当に痛いことです。時にはそれを避けるのが最善です。

パターンと置換文字列、テストファイルをいくつかのファイルに入れてみましょう。

$ cat pat 
$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
$ cat repl
hello!
$ cat test.txt
foo
$GLOBALS['timechecks']=addTimeCheck_sparky($GLOBALS['timechecks'], number_format(microtime(true),6,'.',''), __LINE__, basename(__FILE__));
bar

次に、文字列をPerlに置き換えます。

$ pat=$(< pat) repl=$(< repl) perl -i.bak -pe 's/\Q$ENV{pat}/$ENV{repl}/' test.txt
$ cat test.txt
foo
hello!
bar

ファイルから文字列を読み取るときにシェルコマンドラインから文字列を引用する必要はありません。また、パターンが変数からインポートされ\Q使用される場合、パターンの特殊文字をエスケープする必要はありません。ここでは、文字列はコマンドライン引数よりもうまく機能するため、環境を介してPerlに文字列を渡します-i。 sのように、各入力行に対して指定されたスクリプトを実行するのと同じように動作を-p作成します。perlsed-i.baksed-i

関連質問:ターゲット文字列を入力として受け入れ、それを見つけるための正規表現を提供するジェネレータがないのはなぜですか?

素晴らしい。正規表現は、複数の文字列に一致するように設計されたパターンで使用されることが多く、プログラム内でどの部分が変わるのかを知ることは困難です。常に固定文字列を探している場合は、特殊文字をエスケープする方が簡単です。しかし、実際には正規表現エンジンは最初は必要ありません。一般的なUnixツールでは非常に一般的です。

コメントで次のように言及しました。

考えてみてください。行がその文字列と一致する場合は、それを置き換えるために知っておくべきことは次のとおりです。$GLOBALS['timechecks']=addTimeCheck_sparky

それはまるで

sed -- -e 's/^.*GLOBALS..timechecks..=addTimeCheck_sparky.*$/hello/' 

これを一致させ、ライン全体を交換するために使用できます。もちろん、これは#GLOBALS_atimecheckses=addTimeCheck_sparkyトリックを書き、すべての特殊文字を.。しかし、あなたはポイントを理解しています。

また、元のファイルを最初にバックアップする場合は、いつでもバックアップコピーを作成して実行してdiff original.txt processed.txt変更を確認できます。

おすすめ記事