sed置換パターンの文字列をエスケープする


317

私のbashスクリプトには、外部(ユーザーから受け取った)文字列があり、sedパターンで使用する必要があります。

REPLACE="<funny characters here>"
sed "s/KEYWORD/$REPLACE/g"

$REPLACE文字列をエスケープして、文字列のsed置き換えとして安全に受け入れられるようにするにはどうすればよいですか?

注:KEYWORDこれは、ユーザによって供給されていないいない試合などでダムストリングです。


13
「/ g -e 's / PASSWORD =。* / PASSWORD = abc / g'」と表示された場合、「Little Bobby Tables」の問題を回避しようとしていますか?
ポールトンブリン

2
bashを使用する場合、sedは必要ありません。使用するだけoutputvar="${inputvar//"$txt2replace"/"$txt2replacewith"}".
デステンソン2015

@destenson 2つの変数を引用符の外に置いてはいけないと思います。bashは、二重引用符内の変数を読み取ることができます(例では、空白文字で混乱する可能性があります)。
Camilo Martin

2
次も参照してください:stackoverflow.com/q/29613304/45375
mklement0 2016年

1
@CamiloMartin、私のコメントに対する私のコメントを参照してください。$ {}内の引用符は、内部の引用符と一致しません。2つの変数は引用符の外側にありません
デステンソン

回答:


268

警告:これは改行を考慮しませ。より詳細な回答については、代わりにこのSO質問を参照してください。(ありがとう、エド・モートンとニクラス・ピーター)

すべてをエスケープすることは悪い考えであることに注意してください。Sedは、特別な意味を得るために多くの文字をエスケープする必要があります。たとえば、置換文字列の数字をエスケープすると、後方参照になります。

ベンブランクが言ったように、置換文字列ではエスケープする必要のある文字は3つだけです(エスケープ、エスケープ、ステートメントの終わりにはスラッシュ、すべて置換には&):

ESCAPED_REPLACE=$(printf '%s\n' "$REPLACE" | sed -e 's/[\/&]/\\&/g')
# Now you can use ESCAPED_REPLACE in the original sed statement
sed "s/KEYWORD/$ESCAPED_REPLACE/g"

KEYWORD文字列をエスケープする必要がある場合は、次のものが必要です。

sed -e 's/[]\/$*.^[]/\\&/g'

そして、以下によって使用できます。

KEYWORD="The Keyword You Need";
ESCAPED_KEYWORD=$(printf '%s\n' "$KEYWORD" | sed -e 's/[]\/$*.^[]/\\&/g');

# Now you can use it inside the original sed statement to replace text
sed "s/$ESCAPED_KEYWORD/$ESCAPED_REPLACE/g"

/区切り文字として以外の文字を使用する場合は、使用している文字で上記の式のスラッシュを置き換える必要があることに注意してください。説明については、PeterJCLawのコメントを参照してください。

編集済み:以前は考慮されていなかったいくつかのまれなケースのため、上記のコマンドは何度か変更されました。詳細については、編集履歴を確認してください。


17
スラッシュを区切り文字として使用しないことで、スラッシュをエスケープする必要を回避できることに注意してください。sedのほとんど(すべて?)のバージョンでは、次のパターンに適合する限り、任意の文字を使用できます。$ echo 'foo / bar' | sed s _ / _:_#foo:bar
PeterJCLaw

2
sed -e 's /(\ / \ | \\\ |&)/ \\&/ g'はOSXでは機能しませんでしたが、次のように機能します:sed 's /([\\\ /&])/ \\&/ g 'で、少し短いです。
jcoffland

1
検索パターンについてKEYWORD、でGNU sedはここ2つの以上文字です、^$上に記載されていないが、:s/[]\/$*.^|[]/\\&/g
Peter.O

1
@ジェシー:修正。実際、それは最初の段落で警告した間違いです。私は説教することを実践していないと思います。
ピアノサウルス2014年

1
@NeronLeVelu:意味がわかりませんが、 "はパイプや変数では特別な意味はありません。結果を実行する前にシェルによって解析されるため、変数内の二重引用符は安全です。たとえばA='foo"bar' echo $A | sed s/$A/baz/、 bash。二重引用符は、その周りの「foo」と「bar」のように扱われます
ピアノサウル

92

sedコマンドを使用すると/、セパレータの代わりに他の文字を使用できます。

sed 's#"http://www\.fubar\.com"#URL_FUBAR#g'

二重引用符は問題ではありません。


5
それでも.特別な意味を持つエスケープする必要があります。回答を編集しました。
ypid

私はちょうど行うことを試みている:sed '/CLIENTSCRIPT="foo"/a CLIENTSCRIPT2="hello"' filesed '|CLIENTSCRIPT="foo"|a CLIENTSCRIPT2="hello"' file、それは同じことをしていません。
Dimitri Kopriwa

1
これは置換にのみ適用されるため、これは次のようにsなります。sed のコマンド(置換として)を使用すると、区切り文字として/の代わりに他の文字を使用できます。また、これはスラッシュ文字を含むURLでsedを使用する方法の答えになります。ユーザーが入力した文字列をエスケープする方法に関するOPの質問には答えません。/、\を含めることができますが、それを使用する場合は#も含めることができます。さらに、URIには#も含めることができます
papo 2018

2
それは私の人生を変えました!ありがとうございました!
フランシスコサントス

48

replace句で特別に扱われる3つのリテラル文字は/\(句を閉じるため)、(文字をエスケープするため、後方参照など)、&(一致を置換に含めるため)です。したがって、必要なのはこれらの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

また改行だと思います。改行をエスケープするにはどうすればよいですか?
アレクサンダーグラディッシュ2009年

2
バックスラッシュに関して、エコーのデフォルトの動作が何であるかに注意してください。bashでは、echoのデフォルトはバックスラッシュエスケープの解釈なしであり、ここで目的を果たします。一方、ダッシュ(sh)では、echoはバックスラッシュエスケープを解釈し、私の知る限り、これを抑制する方法はありません。したがって、ダッシュ(sh)では、echo $ xの代わりに、printf '%s \ n' $ xを実行します。
Youssef Eldakar

また、読み取りを行うときは常に-rオプションを使用して、ユーザー入力のバックスラッシュをリテラルとして扱います。
Youssef Eldakar

他のシェルとのクロスプラットフォームの互換性については、sed特殊文字の置き換えについてこのドキュメントを参照してください:grymoire.com/Unix/Sed.html#toc-uh-62
Clayton

2
@Drux 3つの文字は、replace句で唯一の特殊文字です。さらに、パターン節では特別です。
2017年

33

ピアノサウルスの正規表現に基づいて、キーワードと置換の両方をエスケープする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

3
ありがとう!他の誰かが私と同じようにそれを使用しようとしたときに構文エラーが発生した場合は、shではなくbashを使用して実行してください
Konstantin Pereiaslov

1
sedをラップする代わりに、sedの文字列をエスケープするだけの関数はありますか?
CMCDragonkai 2014

こんにちは、次のようなエコーでパイプを開始することに関する一般的な警告です:エコーの一部の(ほとんど?)実装はオプション(を参照man echo)を取り、引数$1がダッシュで始まる場合にパイプが予期せず動作する原因になります。代わりに、でパイプを開始できますprintf '%s\n' "$1"
ピアノサウルス

17

応答するのが少し遅れます...しかし、これを行うにはもっと簡単な方法があります。区切り文字(つまり、フィールドを区切る文字)を変更するだけです。だから、s/foo/bar/あなたの代わりに書いてくださいs|bar|foo

そして、これを行う簡単な方法は次のとおりです。

sed 's|/\*!50017 DEFINER=`snafu`@`localhost`\*/||g'

結果の出力には、厄介なDEFINER句がありません。


10
いいえ、&および ``は、デリミタと同じように、エスケープする必要があります。
mirabilos 2014年

3
置換文字列に「/」文字が含まれていたため、これで問題が解決しました。ありがとう!
Evgeny Goldin 2014年

私のために働く。$変更しようとしている文字列でエスケープして$、置換文字列の意味を維持しようとしています。$XXXvariableの値に変更したい場合$YYY、問題なくsed -i "s|\$XXX|$YYY|g" file動作します。
白波2014年

11

間違った質問をしていることがわかりました。私も間違った質問をしました。それが間違っている理由は、最初の文の始まりです:「私のbashスクリプトで...」。

私は同じ質問をし、同じ間違いをしました。もし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"}"

ところで、ここでの構文の強調表示は間違っています。外部の引用が一致し、内部の引用が一致します。言い換えれば、それは次のようになります$Aし、$B引用符で囲まれていないですが、そうではありません。内部の引用符は、${}外部の引用符と一致しません。
デステンソン

実際に課題の右側を引用する必要はありません(のようなことをしたくない場合を除くvar='has space')– OUTPUT=${INPUT//"$A"/"$B"}安全です。
ベンジャミンW.

実際に課題の右側を引用する必要はありません(それが現実の世界で機能することを望んでいない場合や、yur mad skilzを表示するおもちゃのスクリプトとしてだけではありません)。私は、特別な理由がない限り、常にシェルに解釈させたくないすべての変数展開を引用しようとします。そうすれば、特に新しい入力や予期しない入力が提供されたときに、物事が壊れる頻度が減ります。
デステンソン2017年

1
マニュアルを参照してください:「すべての値は、チルダ展開、パラメーターと変数の展開、コマンドの置換、算術展開、引用符の削除(詳細は下記)を行います。」つまり、二重引用符と同じです。
ベンジャミンW.

1
ファイルでsedを使用する必要がある場合はどうなりますか?
2018

1

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

2
の問題awkは、に似ていないことですsed -i。99%の時間で非常に便利です。
ティノ

これは正しい方向へのステップですが、awkはまだ置換の一部のメタ文字を解釈するため、ユーザー入力に対してはまだ安全ではありません。
Jeremy Huiskamp、

0

これは、少し前に使用したAWKの例です。新しいAWKSを印刷するAWKです。AWKとSEDは似ているので、良いテンプレートかもしれません。

ls | awk '{ print "awk " "'"'"'"  " {print $1,$2,$3} " "'"'"'"  " " $1 ".old_ext > " $1 ".new_ext"  }' > for_the_birds

それは過度に見えますが、どういうわけか引用符の組み合わせは 'をリテラルとして出力し続けるために機能します。次に、私が正しく覚えている場合、変数は「$ 1」のような引用符で囲まれています。試してみて、SEDでどのように機能するかを教えてください。


0

タブのような特殊文字で壊れる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


しかし、パイプでのエコーの使用に関する緊急の答えについての私のコメントを参照してください。
Pianosaurus

0

これらは私が見つけたエスケープコードです:

* = \x2a
( = \x28
) = \x29

" = \x22
/ = \x2f
\ = \x5c

' = \x27
? = \x3f
% = \x25
^ = \x5e

-1

"と 'に関するシェルの制限によって発生するすべての喜びを忘れないでください

そう(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"

検索結果をエスケープするためにGoogleで見つけた、正確に私が必要だった方向/ g '
MolbOrg 2018年

-1

sedコマンドの変数値を置き換えるだけの場合は、例を削除します。

sed -i 's/dev-/dev-$ENV/g' test to sed -i s/dev-/dev-$ENV/g test

-2

sed置換パターンに渡すランダムパスワードを生成している場合は、ランダム文字列のどの文字セットに注意するかを選択します。値をbase64としてエンコードすることによって作成されたパスワードを選択した場合、base64で可能な文字とsed置換パターンの特殊文字の両方が存在します。その文字は「/」であり、生成するパスワードから簡単に削除できます。

# password 32 characters log, minus any copies of the "/" character.
pass=`openssl rand -base64 32 | sed -e 's/\///g'`;

-4

これを行う簡単な方法は、事前に文字列を作成し、それをパラメータとして使用することです。 sed

rpstring="s/KEYWORD/$REPLACE/g"
sed -i $rpstring  test.txt

失敗と非常に危険、などREPLACEユーザー供給される: REPLACE=/与えsed: -e expression #1, char 12: unknown option to `s'
ティノ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.