Bash環境でsedコマンドをネストされた部分に分割する方法は?

Bash環境でsedコマンドをネストされた部分に分割する方法は?

私はCentOS Bash環境で作業しており、sedプロのシステム管理者ではない人としてsed少し「混乱する」ほど長いコマンドを持っており、少なくとも2年に1回、少なくとも長いコマンドとして使用します。

read new_email_address
sed -i 's/$to = ".*";$/$to = "'"$new_email_address"'";/' FILE

sedコマンドを次のネストした擬似コードに分割したいと思います。

sed -i
    '
        s/
            $to = ".*";$
        /
            $to = "'"$new_email_address"'";
        /g
    '
FILE_PATH

答え1

ここではこれを使いますperl-i非標準オプションなので、一部のsed実装ではそれをコピーしましたが、perl移植可能ではありません。この方法を使用することもsedコマンド注入の脆弱性です。内容が$new_email_address最終的にsedコードとして解釈されるためです(GNU言語には、そのプロンプトに入力しようとするなど、sed任意のコマンドを実行できるコマンドがあります)。/;ereboot;#read

IFS= read -r new_email_address

REPLACEMENT="$new_email_address" perl -pi -e '
  s{
     (\$to \s* = \s* ") .* (" \s* ; \s* )$
   }{$1$ENV{REPLACEMENT}$2}gx
 ' FILE

存在するperl

  • s{...}{...}flagsextraを使用すると、一致するs/.../.../flagsペアをより簡単に確認できます(そして一致する限り、内部使用も許可します){}
  • このxフラグを使用すると、正規表現内にスペース(またはコメント)を追加して読みやすくすることができます(これらのスペースは正規表現の一部ではありませんが、\s*任意の数のスペースと一致します)。
  • &環境変数を使用して、、バックスラッシュ、または改行文字を含む文字列を渡す場合でも、置換に任意の文字列を安全に使用できます。/
  • -C// -Mlocale...-Mopen=localeオプションを使用しない限りperl、バイトレベルで作業するため、入力が.*ロケールで有効なテキストを形成しない場合でも、一致は失敗しません。
  • 一部のsed実装とperlは異なり、行の長さ(使用可能なメモリを除く)に制限はなく、NULバイトを含むか改行文字で終わらない入力をブロックしません。

交換部品にも空白を許可するには、e交換部品をコードにするフラグを追加できますperl

REPLACEMENT="$new_email_address" perl -pi -e '
  s{
     (\$to \s* = \s* ") .* (" \s* ; \s* )$
   }{
     $1 . $ENV{REPLACEMENT} . $2
   }gxe
 ' FILE

例えば。また、read設定なし$IFSとなしを使用することは-rほとんど意味がありません。

答え2

もっと読みやすいかどうかはわかりませんが、sedまずビルド式を使用してprintfから次のように使用できますsed

sed_expr=$(printf 's/$to = ".*";$/$to = "%s";/' "$new_email_address")
sed -i "$sed_expr" FILE

私の考えでは、これはsedの全体的な機能と入力がどのように機能するかを理解するのが簡単です。

答え3

引用されたコマンドは次のとおりです。私は最近の回答に書きました。(追加-i):

sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file

これはsed単一の編集コマンドで呼び出されます。式で使用されるコマンドは、置換を実行するコマンドsedですs。つまり、正規表現と一致するものを別のものに置き換えます。

このコマンドの一般的な形式sはですrange s/pattern/replacement/flags。ここで扱うコマンドには表現はrangeありません。sみんな入力テキストの行)とnoですflags。したがって、sed通常のフォームに編集スクリプトがあります。

s/pattern/replacement/

コマンドが示すように、ビットpatternは次のようになります。

$to = ".*";$

$to = "このパターンは、次のリテラルテキストと一致します。何もない(長さに関係なくすべての文字のシーケンス)、その後にリテラルテキストが続きます";$at the end は、最後の数字が行の最後";に一致するように強制します。

そして私たちはそれを持っていますreplacement

シェル変数の値に依存するものに置き換えたいので、しばらく一重引用符で囲まれた文字列(つまり式)から外す必要がありますsed。私たちはこれをした後

$to = "

交換中です。シェル変数の値はnew_email_address挿入され、二重引用符で正しく引用されているため、シェルはそれをスペースに分割したり、その値に対してファイル名のグロービングを実行したりしません。

値を挿入したら、次のコマンドでコマンドreplacementセクションを終了します。s

";

これはフィールドreplacement全体で、$to = "その後にいくつかの値(新しいEメールアドレス)が続きます";

したがって、命令の各ビットが実行し、何を意味するのかを分析し、明確に説明します。

コマンド構造sed:

    sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
         s/  pattern   /          replacement          /

sedシェルで式を構成する文字列ビット:

    sed 's/$to = ".*";$/$to = "'"$new_email_address"'";/' file
         ^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^^^  ^^^
         single-quoted string    double-quoted       final single-quoted bit
                                 string for shell
                                 variable expansion

上記をもっと模式的に

    sed 'something here'"$variable_value_here"'ending here' file

ビットsomething hereは二重引用符で終わり、ending hereビットは二重引用符で始まります。

答え4

引用符を区切って保持する1つの方法は、複数の-e sedコードを使用して検索と置換を分離してsedコマンドを中断することです。

q=\"; # a double quote character
sed -i \
    -e '/$to = ".*";$/c\'  \
    -e "\$to = $q$new_email_address$q;" \
FILE

sed -i \
    -e '/$to = ".*";$/!b' \
    -e "s//\$to = $q$new_email_address$q;/" \
FILE;

関連情報