sed replaceパターンの文字列をエスケープします
質問
bashスクリプトには、外部(ユーザーから受け取った)文字列があり、sedパターンで使用する必要があります。
REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"
$ REPLACE
文字列をエスケープして、 sed
がリテラル置換として安全に受け入れられるようにするにはどうすればよいですか?
注: KEYWORD
は、一致しないなどのダム部分文字列です。ユーザーからは提供されません。
解決
警告:これは、改行を考慮しません 。より詳細な回答については、このSO質問で正規表現のメタキャラクターを確実にエスケープすることができます。 (ありがとう、エドモートン&ニクラスピーター)
すべてをエスケープするのは悪い考えです。 Sedは、特別な意味を得るために多くの文字をエスケープする必要があります。たとえば、置換文字列の数字をエスケープすると、後方参照になります。
Ben Blankが言ったように、置換文字列でエスケープする必要があるのは3文字のみです(それ自体をエスケープし、ステートメントの終わりにはスラッシュを、すべてを置換するには&amp;):
sed -e 's/[\/&]/\\&/g'
KEYWORD
文字列をエスケープする必要がある場合、必要なものは次のとおりです。
sed -e 's/[]\/$*.^[]/\\&/g'
/
以外の文字を区切り文字として使用する場合は、使用している文字で上記の式のスラッシュを置き換える必要があります。説明については、PeterJCLawのコメントを参照してください。
編集済み:以前は考慮されていなかったいくつかのコーナーケースのため、上記のコマンドは数回変更されました。詳細については、編集履歴を確認してください。
他のヒント
sedコマンドを使用すると、セパレータとして /
の代わりに他の文字を使用できます。
sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'
二重引用符は問題ではありません。
replace句で特別に処理される3つのリテラル文字は、 /
(句を閉じる)、 \
(文字をエスケープする、後方参照、&amp; c 。)、および&amp;
(置換に一致を含めるため)。したがって、これらの3つの文字をエスケープするだけです。
sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
例:
$ export REPLACE="'\"|\\/><&!"
$ echo fooKEYWORDbar | sed "s/KEYWORD/$(echo $REPLACE | sed -e 's/\\/\\\\/g; s/\//\\\//g; s/&/\\\&/g')/g"
foo'"|\/><&!bar
Pianosaurusの正規表現に基づいて、キーワードと置換の両方をエスケープするbash関数を作成しました。
function sedeasy {
sed -i "s/$(echo $1 | sed -e 's/\([[\/.*]\|\]\)/\\&/g')/$(echo $2 | sed -e 's/[\/&]/\\&/g')/g" $3
}
使用方法は次のとおりです。
sedeasy "include /etc/nginx/conf.d/*" "include /apps/*/conf/nginx.conf" /etc/nginx/nginx.conf
応答に少し遅れました...しかし、これを行うにはもっと簡単な方法があります。区切り文字(つまり、フィールドを区切る文字)を変更するだけです。したがって、 s / foo / bar /
の代わりに、 s | bar | foo
と記述します。
そして、これを行う簡単な方法は次のとおりです:
sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'
結果の出力には、その厄介なDEFINER句がありません。
間違った質問をしていることがわかります。私も間違った質問をしました。間違っている理由は、最初の文の始まりです:&quot; bash スクリプト...&quot;。
同じ質問がありました&amp;同じ間違いを犯した。 bashを使用している場合、文字列の置換にsedを使用する必要はありません(bashに組み込まれている置換機能を使用する方がずっときれいです)。
たとえば、次のようなものの代わりに:
function escape-all-funny-characters() { UNKNOWN_CODE_THAT_ANSWERS_THE_QUESTION_YOU_ASKED; }
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A="$(escape-all-funny-characters 'KEYWORD')"
B="$(escape-all-funny-characters '<funny characters here>')"
OUTPUT="$(sed "s/$A/$B/g" <<<"$INPUT")"
bash機能のみを使用できます:
INPUT='some long string with KEYWORD that need replacing KEYWORD.'
A='KEYWORD'
B='<funny characters here>'
OUTPUT="${INPUT//"$A"/"$B"}"
awkを使用します-よりクリーンです:
$ awk -v R='//addr:\\file' '{ sub("THIS", R, <*>); print <*> }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare
ここで私が少し前に使用したAWKの例を示します。新しいAWKSを印刷するAWKです。 AWKとSEDは似ているので、良いテンプレートかもしれません。
ls | awk '{ print "awk " "'"'"'" " {print $1,$2,$3} " "'"'"'" " " $1 ".old_ext > " $1 ".new_ext" }' > for_the_birds
それは過剰に見えますが、どういうわけか引用符の組み合わせは、 'をリテラルとして出力し続けるように機能します。正しく覚えていれば、変数は次のような引用符で囲まれているだけです:&quot; $ 1&quot;。それを試して、SEDでどのように機能するか教えてください。
sedeasy機能よりも改善されています。sedeasy機能は、tabなどの特殊文字で壊れます。
function sedeasy_improved {
sed -i "s/$(
echo "$1" | sed -e 's/\([[\/.*]\|\]\)/\\&/g'
| sed -e 's:\t:\\t:g'
)/$(
echo "$2" | sed -e 's/[\/&]/\\&/g'
| sed -e 's:\t:\\t:g'
)/g" "$3"
}
では、何が違うのですか? $ 1
および $ 2
は引用符で囲み、シェルの展開を避け、タブまたはダブルスペースを保持します。
追加の配管 | sed -e 's:\ t:\\ t:g'
(トークンとして:
が好き) \ t
のタブを変換します。
&quot;の周りのシェルの制限で生じるすべての喜びを忘れないでください。および '
so(in ksh)
Var=">New version of \"content' here <"
printf "%s" "${Var}" | sed "s/[&\/\\\\*\\"']/\\&/g' | read -r EscVar
echo "Here is your \"text\" to change" | sed "s/text/${EscVar}/g"
sed
置換パターンに渡すランダムパスワードを生成している場合は、ランダム文字列のどの文字セットに注意するかを選択します。値をbase64としてエンコードして作成されたパスワードを選択した場合、base64で可能な文字と、 sed
置換パターンの特殊文字のみがあります。その文字は&quot; /&quot;であり、生成するパスワードから簡単に削除できます。
# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;
sedコマンドの変数値を置き換えるだけの場合は、削除するだけです 例:
sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test
これを行う簡単な方法は、単に文字列を事前に構築し、それを sed
rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring test.txt