テキストストリームを読み込み、後で使用できるようにsedコマンドファイルを生成するスクリプトがありますsed -f
。生成されたsedコマンドは次のとおりです。
s/cid:image002\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1922/g
s/cid:image003\.gif@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1923/g
s/cid:image004\.jpg@01CC3D46\.926E77E0/https:\/\/mysite.com\/files\/1924/g
コマンドを生成するスクリプトが次のとおりですsed
。
while read cid fileid
do
cidpat="$(echo $cid | sed -e s/\\./\\\\./g)"
echo 's/'"$cidpat"'/https:\/\/mysite.com\/files\/'"$fileid"'/g' >> sedscr
done
cid
文字列内のすべての正規表現メタ文字が正しくエスケープされ補間されるようにスクリプトを改善するにはどうすればよいですか?
ベストアンサー1
使用するエスケープ変数左そして右s
コマンドに対してsed
(ここ$lhs
と$rhs
それぞれ)次の操作を行います。
escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\\/.^$*]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\\/&]:\\&:g; $!s/$/\\/')
sed "s/$escaped_lhs/$escaped_rhs/"
改行は$lhs
含めることはできません。
つまり、LHSでは、すべての正規表現演算子(][.^$*
)、エスケープ文字自体()、\
および区切り文字(/
)がエスケープされます。
&
RHSでは、エスケープ、区切り文字、バックスラッシュ、および改行($!s/$/\\/
最後の行()を除くすべての行の末尾にバックスラッシュを挿入することによって達成されます)のみが必要です。
注:文字の前にバックスラッシュを追加したくありません。いいえ特別な意味があります。だからこうすれば結局与える特別な意味があります。たとえば、、、およびBREでは特別な意味はありませんが、、および(およびRHSを含むfor)は<
いくつか+
の実装では特別な意味を持ちます。t
\<
\+
\t
sed
\t
/
コマンドで区切り文字として使用しsed
s
てアクティブにしないとします。拡張 RE-r
(GNU sed
// ssed
/ ast
)または(BSD、最も近いGNU、最も近いビジボックス)busybox sed
または-E
ast
PCRE(-R
)ssed
またはRE強化-A
/ -X
()のようにast
追加のRE演算子があります。
ERE(これらの拡張の中で最も広くサポートされている拡張)の場合、その機能は次のとおりです。
escaped_lhs=$(printf '%s\n' "$lhs" | sed 's:[][\\/.^$*+?(){}|]:\\&:g')
escaped_rhs=$(printf '%s\n' "$rhs" | sed 's:[\\/&]:\\&:g; $!s/$/\\/')
sed -E "s/$escaped_lhs/$escaped_rhs/"
任意のデータを扱う際の基本的な規則は次のとおりです。
- 使用しないでください
echo
- 変数参照
- ロケール(特に文字セット)の影響を検討してください。逃げる
sed
sed
コマンドは、コマンドと同じロケールで実行されます。使用これ脱出するsed
例:文字列(同じコマンドを使用) $lhs
改行文字を忘れないでください(ここで改行文字が含まれていることを確認して対処できます)。
より安全なオプションは、環境に文字列を渡すperl
のではなく、/正規表現演算子を使用して文字列を文字通り取得することです。sed
\Q
\E
perl
A="$lhs" B="$rhs" perl -pe 's/\Q$ENV{A}\E/$ENV{B}/g'
perl
(デフォルトでは)はロケール文字セットの影響を受けません。上記は、文字列をバイト配列としてのみ処理し、ユーザーに表示できる文字(ある場合)については気にしないからです。を使用すると、すべてのコマンドsed
のロケールをwithに変更してC
同じ結果を得ることができます。ただし、これはエラーメッセージの言語にも影響します。LC_ALL=C
sed
一部のシェルでは、外部ユーティリティを使用せずにエスケープすることもできます。
zsh
(BREエスケープの場合はこちら):
set -o extendedglob
escaped_lhs=${lhs//(#m)[][\\.^$\/&]/\\$MATCH}
escaped_rhs=${rhs//(#m)[\\&\/$'\n']/\\$MATCH}
存在するksh93
:
escaped_lhs=${lhs//[][\\.^$\/&]/\\\0}
escaped_rhs=${rhs//[\\&\/$'\n']/\\\0}
3.4.0+からfish
:
set escaped_lhs (
string replace -ar -- '[][\\\\/.^$*]' '\\\\$0' "$lhs" |
string collect --allow-empty
)
set escaped_rhs (
string replace -ar -- '[\\\\&/'\n']' '\\\\$0' "$rhs" |
string collect --allow-empty --no-trim-newlines
)