私のbashスクリプトには、(ユーザーから受け取った)外部文字列があります。これはsedパターンで使用します。
REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"
sed
がリテラルの置き換えとして安全に受け入れられるように、$REPLACE
文字列をエスケープするにはどうすればよいですか?
注:KEYWORD
は、一致などしない、ダムな部分文字列です。ユーザーからは提供されません。
警告:これはしません改行を考慮します。より詳細な答えについては、代わりに このSO-question を参照してください。 (ありがとう、エドモートン&ニクラスピーター)
すべてをエスケープするのは悪い考えです。 Sedは、getにエスケープするために多くの文字を必要とします。たとえば、置換文字列で数字をエスケープすると、後方参照になります。
Ben Blankが言ったように、置換文字列にエスケープする必要があるのは3文字だけです(自分自身をエスケープし、ステートメントの終わりにはスラッシュ、そしてすべて置換には&を使います)。
sed -e 's/[\/&]/\\&/g'
KEYWORD
という文字列をエスケープする必要がある場合は、次のものが必要です。
sed -e 's/[]\/$*.^[]/\\&/g'
/
以外の文字を区切り文字として使用する場合は、使用している文字で上記の式のスラッシュを置き換える必要があります。 PeterJCLawの説明を見てください。
編集:以前は考慮されていなかったいくつかのコーナーケースが原因で、上記のコマンドが何度か変更されました。詳細は編集履歴を確認してください。
Sedコマンドを使用すると、/
の代わりに他の文字を区切り文字として使用できます。
sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'
二重引用符は問題になりません。
Replace句で特別に扱われる3つのリテラル文字は、/
(句を閉じるため)、\
(文字をエスケープするため、後方参照、&c。)、および&
(置換に一致を含めるため)です。したがって、あなたがする必要があるのはこれらの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
ピアノサウルスの正規表現に基づいて、私はキーワードと置換の両方を回避する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
返答するのは少し遅れますが…ISこれをするためのもっと簡単な方法があります。区切り文字(つまり、フィールドを区切る文字)を変更するだけです。そのため、s/foo/bar/
の代わりにs|bar|foo
を書きます。
そして、これを行う簡単な方法は次のとおりです。
sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'
結果の出力には、その厄介なDEFINER句はありません。
それはあなたが間違った質問をしているのです。私も間違った質問をしました。それが間違っている理由は最初の文の始まりです: "私のbash script ..."。
私は同じ質問をし、同じ過ちを犯しました。 bashを使っているのであれば、文字列を置き換えるのにsedを使う必要はありません(そしてbashに組み込まれたreplace機能を使うほうがmuchよりきれいです)。
次のようなものではなく、
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, $0); print $0 }' <<< "http://file:\_THIS_/path/to/a/file\\is\\\a\\ nightmare"
http://file:\_//addr:\file_/path/to/a/file\\is\\\a\\ nightmare
これは私がしばらく前に使ったAWKの例です。新しいAWKを印刷するのはAWKです。 AWKとSEDは似ているので良いテンプレートかもしれません。
ls | awk '{ print "awk " "'"'"'" " {print $1,$2,$3} " "'"'"'" " " $1 ".old_ext > " $1 ".new_ext" }' > for_the_birds
それは過剰に見えますが、どういうわけか引用符の組み合わせは 'リテラルとして印刷されたものを保つのに役立ちます。それで私が正しく覚えているならば、可変要素はちょうどこのような引用符で囲まれています: "$ 1"。試してみて、それがSEDでどのように機能するのか教えてください。
「と」の前後のシェルの制限によって生じるすべての喜びを忘れないでください。
そう(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
replaceパターンに渡すためにランダムなパスワードを生成しているという場合は、ランダムな文字列のどの文字セットに注意するかを選択します。値をbase64としてエンコードして作成されたパスワードを選択した場合は、base64で使用可能な文字と、sed
replaceパターンでの特殊文字の両方が使用可能です。その文字は "/"であり、生成しているパスワードから簡単に削除されます。
# password 32 characters log, minus any copies of the "/" character.
pass=`openssl Rand -base64 32 | sed -e 's/\///g'`;
タブのような特殊文字で壊れるsedeasy機能を改良しました。
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
が好きです)。