web-dev-qa-db-ja.com

shスクリプトでsedを使用する場合、どの文字をエスケープする必要がありますか?

次のスクリプトを見てください。

#!/bin/sh
sed 's/(127\.0\.1\.1)\s/\1/' [some file]

これをsh(ここではdash)で実行しようとすると、エスケープする必要がある括弧が原因で失敗します。しかし、私はしないでくださいバックスラッシュ自体をエスケープする必要があります(オクテット間、または\sまたは\1内)。ここでのルールは何ですか? {...}または[...]を使用する必要がある場合はどうなりますか?私がしなければならないことのリストはありますか?

271
detly

ここでは2つのレベルの解釈があります。シェルとsedです。

シェルでは、一重引用符自体を除いて、一重引用符の間のすべてが文字どおりに解釈されます。 '\''(単一引用符を閉じる、リテラル単一引用符を1つ、単一引用符を開く)を書くことにより、単一引用符の間に単一引用符を効果的に置くことができます。

Sedは 基本正規表現 を使用します。 BREでは、文字セットを文字どおりに処理するために、文字セット($.*[\^)を除いて、文字[…]の前にバックスラッシュを付けて引用符で囲む必要があります。文字、数字、および(){}+?|は引用符で囲まないでください(一部の実装ではこれらの一部を引用符で囲むことで回避できます)。シーケンス\(\)\n、および一部の実装では\{\}\+\?\|およびその他のバックスラッシュ+英数字には特別な意味があります。実装によっては、$^を引用符で囲まずに済む場合があります。

さらに、/の前にバックスラッシュが必要な場合は、ブラケット式の外の正規表現で使用する必要があります。 s~/dir~/replacement~または\~/dir~p;のように記述することにより、区切り文字として代替文字を選択できます。区切り文字をBREに含める場合は、区切り文字の前にバックスラッシュが必要です。 BREで特別な意味を持つ文字を選択し、それを文字通り含めたい場合は、3つの円記号が必要です。一部の実装では動作が異なる可能性があるため、これはお勧めしません。

簡単に言えば、sed 's/…/…/'の場合:

  • 単一引用符の間に正規表現を記述します。
  • 正規表現で単一引用符を使用するには、'\''を使用します。
  • $.*/[\]^の前にバックスラッシュを置き、それらの文字のみを入れます(ただし、ブラケット式の中には入れません)。 (技術的には]の前にバックスラッシュを置くべきではありませんが、]\]をブラケット式の外側で異なる方法で処理する実装については知りません。)
  • ブラケット式の内側で、-を文字どおりに処理するには、それが最初または最後であることを確認してください([abc-]または[-abc]、 [a-bc])。
  • ブラケット式の内側で、^を文字どおりに処理するには、最初にnotであることを確認してください([abc^]を使用して、 [^abc])。
  • 大括弧式に一致する文字のリストに]を含めるには、最初の文字(または否定セットの場合は^の後に最初に)にしてください:[]abc]または[^]abc](ない [abc]]または[abc\]])。

置換テキスト:

  • &および\は、区切り文字(通常は/)および改行と同様に、バックスラッシュを前に付けて引用する必要があります。
  • \の後に数字が続く場合は、特別な意味があります。一部の実装では、\の後に文字が続くと特別な意味(特殊文字)があり、\の後に他の文字が続く場合は、実装に応じて\cまたはcを意味します。
  • 引数を一重引用符で囲んで(sed 's/…/…/')、'\''を使用して置換テキストに一重引用符を入れます。

正規表現または置換テキストがシェル変数からのものである場合は、

  • 正規表現はBREであり、リテラル文字列ではありません。
  • 正規表現では、改行は\nとして表す必要があります(パターンスペースに改行文字を追加する他のsedコードがない限り、これは決して一致しません)。ただし、一部のsed実装では、ブラケット式内では機能しないことに注意してください。
  • 置換テキストでは、&\および改行を引用符で囲む必要があります。
  • 区切り文字は引用符で囲む必要があります(ただし、ブラケット式の内側は不要です)。
  • 補間には二重引用符を使用してください:sed -e "s/$BRE/$REPL/"

発生している問題は、シェルの補間とエスケープが原因ではありません。これは、sed -rまたは--regexp-extendedオプションを渡さずに拡張正規表現構文を使用しようとしているためです。

からセッドラインを変更する

sed 's/(127\.0\.1\.1)\s/\1/' [some file]

sed -r 's/(127\.0\.1\.1)\s/\1/' [some file]

そしてそれは私があなたが意図していると信じているように機能します。

デフォルトでは、sedは基本的な正規表現を使用します(grepスタイルを考えてください)。これには次の構文が必要です。

sed 's/\(127\.0\.1\.1\)[ \t]/\1/' [some file]
45
R Perrin

Shell変数をsed式に補間する必要がない限り、バックスラッシュを含め、それらの間のすべてがそのまま解釈されるため、式全体に一重引用符を使用します。

そのため、sedでs/\(127\.0\.1\.1\)\s/\1/を表示したい場合は、単一引用符で囲み、シェルは括弧やバックスラッシュに触れません。シェル変数を補間する必要がある場合は、その部分のみを二重引用符で囲みます。例えば。

sed 's/\(127\.0\.1\.1\)/'"$ip"'/'

これにより、二重引用符でエスケープされていないシェルメタキャラクターを覚えておく手間が省けます。

18
Kyle Jones

Sedは基本的な正規表現(BRE)のサポートのみを指定するPOSIX標準に基づいていますが、実際にはBSD(Mac OS)とGNU(Linuxディストリビューション)の2つの異なるバージョンのsedコマンドが存在することは言及に値します。 。各バージョンは、POSIX標準に類似した独自の拡張機能を実装しており、異なるプラットフォーム間でのsedの機能に影響を与える可能性があります。その結果、1つのシステムで期待どおりに機能するsedコマンドの適切な構文は、実際には別のシステムでは完全に異なる結果に変換される可能性があります。これにより、エスケープ文字や特殊文字の使用に関して予期しない動作が発生する可能性があります。

POSIX標準に対するこれらの拡張は、sedのGNUバージョンでより一般的である傾向があり、多くの場合、特にBSDバージョンと比較して、厳密でないフォーマットの利便性を提供します。ただし、GNU sedでは一部の特殊文字の機能は許可されていますが、実際にはPOSIXに準拠していません。さらに、GNU sed内の基本正規表現と拡張正規表現(ERE)の唯一の実際の違いは、次の特殊文字の動作です。

‘?’、 ‘+’、括弧、中括弧( ‘{}’)、 ‘|’

これが当てはまる場合もありますが、「|」、「?」、「+」など、一部の特殊文字はBSD sedで制限されているか、まったくサポートされていません。POSIX構文標準により厳密に準拠しているためです。 GNU sedと同様の方法でこれらの文字を含めると、sedを使用するスクリプトの移植性と機能に問題が発生することがよくあります。また、注目に値します。POSIXBRE構文は、いくつかのエスケープシーケンスの意味を定義していません。特に、\ |、+、\ ?、 `、\ '、\ <、>、\ b、\ B、\ w、\です。 W 、.

BSD/Mac OSバージョンのsedを実行している場合、一部の特殊文字の動作をエミュレートするのは少し難しいかもしれませんが、ほとんどの場合は可能です。たとえば、+は、次のようなPOSIX準拠の方法でエミュレートできます:{1、}および\? {0,1}のようになります。ただし、制御文字シーケンスは通常サポートされていません。可能であれば、GNU sedを使用するのが最も簡単ですが、両方のプラットフォームで機能が必要な場合は、移植性を確保するために、POSIX機能のみを使用することを忘れないでください。 Macユーザーで、BSD sedではなくGNU sedを利用したい場合は、Homebrewをインストールして、コマンドラインからGNU sedをダウンロードしてみてください:$ bnu install gnu-sed。

まとめると、バージョンの違いによって、適切な構文がどのように見えるか、またはエスケープするために必要な文字が決まる場合があります。これにより、最初の質問と承認された回答にいくつかの追加のコンテキストが提供され、スクリプトとコマンドの使用法の最終目標に基づいて、他の人がどのように進むべきかを検討するのに役立つことを願っています。

0
forthelulz