3

このサイトには、sed のさまざまな要素をエスケープする方法について多くの質問がありますが、より一般的な回答を探しています。シェルの拡張を避けるために、一部の文字をエスケープしたい場合があることを理解しています。

バッシュ

  • 単一引用符で囲まれた [文字列] ('') は、引用符で囲まれた各文字のリテラル値を保持するために使用されます。[ただし、]バックスラッシュが前にある場合でも、単一引用符の間に単一引用符が発生しない場合があります
  • バックスラッシュは、その後にドルバックティック二重引用符バックスラッシュ、または改行が続く場合にのみ、 [二重引用符で囲まれた文字列で]その意味を保持します。二重引用符内では、これらの文字のいずれかが後に続く場合、バックスラッシュは入力ストリームから削除されます。特別な意味を持たない文字の前にあるバックスラッシュは、シェル インタープリターによる処理のために変更されずに残されます。

sh : (履歴拡張がないことを願っています)

  • 一重引用符で囲まれた文字列の動作: bash と同じ
  • 文字を二重引用符で囲むと、ドル一重引用符バックスラッシュ、および履歴拡張が有効な場合は感嘆符を除いて、引用符内のすべての文字のリテラル値が保持されます。
    • 文字dollar重引用符は、二重引用符内での特別な意味を保持します。
    • バックスラッシュは、次のいずれかの文字が続く場合にのみ、特別な意味を保持します: $'"\、または改行。二重引用符は、その前にバックスラッシュを付けることにより、二重引用符内で引用できます。
    • 有効にすると、二重引用符で囲まれた感嘆符 がバックスラッシュを使用してエスケープされない限り、履歴の展開が実行されます。! の前のバックスラッシュ 削除されません。

...しかし、エスケープを削除するとすぐにこれが機能しなくなる理由を説明するものはありません:

sed -e "s#\(\w\+\) #\1\/#g" #find a sequence of characters in a line
#    why? ↑   ↑ ↑     ↑     #replace the following space with a slash.

()/または+(または[、または...) のいずれ]も、機能するためにエスケープする必要がある特別な意味を持っているようには見えません。地獄、Python からコマンドを直接呼び出しても sed は正しく動作しませんが、マンページにはこれについて何も詳しく説明されていないようです (とにかく、バックスラッシュを検索したときではありません)。

$ lvdisplay -C --noheadings -o vg_name,name > test
$ python
>>> import os
>>> #Python requires backslash escaping of \1, even in triple quotes
>>> #lest \1 is read to mean "byte with value 0x01".
>>> output = os.execl("/bin/sed", "-e", "s#(\w+) #\\1/#g", "test")
(Output remains unchanged)
$ python
>>> import os
>>> output = os.execl("/bin/sed", "-e", "s#\(\w\+\) #\\1\/#g", "test")
(Correct output)
$ WHAT THE HELL
Have you tried using jQuery? It's perfect and it does all the things.
4

4 に答える 4

4

私があなたを正しく理解していれば、あなたの問題は bash/sh に関するものではなく、sed がデフォルトで使用する正規表現フレーバーに関するものです: BRE

他の [=ドット、スター、キャレット、ドル以外] BRE メタ文字には、特別な意味を与えるためにバックスラッシュが必要です。その理由は、UNIX の最も古いバージョンがgrepこれらをサポートしていなかったためです。

グループ化(..)は、特別な意味を持たせるためにエスケープする必要があります。それ以外の場合と同じよう+に、それらはリテラル文字列/文字であるため、sed はそれらを一致させようとします。s#\(\w\+\) #...#それがあなたがエスケープされるべき理由です。交換部分はエスケープする必要がないため、次のようになります。

sed 's#\(\w\+\) #\1 /#' 

動作するはずです。

sed通常、拡張正規表現を使用するオプションがあります(現在は?, +, |, (),を使用{m,n})。たとえば、GNU sed has-rの場合、ワンライナーは次のようになります。

sed -r 's#(\w+) #\1 /#'

何が起こっているのかを理解するのに役立ついくつかの例をここに貼り付けます。

kent$  echo "abcd "|sed 's#\(\w\+\) #\1 /#'
abcd /
kent$  echo "abcd "|sed -r 's#(\w+) #\1 /#'                                                                                                                                 
abcd /
kent$  echo "(abcd+) "|sed 's#(\w*+) #&/#'
(abcd+) /
于 2013-09-12T08:51:55.027 に答える
1
于 2013-09-12T08:52:24.150 に答える
-1

sedMac OS X でも使用されるFreeBSDは、拡張正規表現-Eの代わりに使用します。-rしたがって、移植性を持たせるには、基本的な正規表現を使用してください。+たとえば、拡張正規表現モードでは\{1,\}、基本正規表現モードで置き換える必要があります。基本正規表現モードと拡張正規表現モードでは、FreeBSDはどれを置き換える必要があるかsedを認識していないようです(参照)。\w[[:alnum:]_]man re_format

# using FreeBSD sed (on Mac OS X)

# output: Hello, world!
echo 'hello    world' | sed -e 's/h/H/' -e 's/ \{1,\}/, /g' -e 's/\([[:alnum:]_]\{1,\}\)$/\1!/'
echo 'hello    world' | sed -E -e 's/h/H/' -e 's/ +/, /g' -e 's/([[:alnum:]_]+)$/\1!/'
echo 'hello    world' | sed -E -e 's/h/H/' -e 's/ +/, /g' -e 's/(\w+)$/\1!/'  # does not work

# find a sequence of characters in a line
# replace the following space with a slash
# output: abcd+/abcd+/
echo 'abcd+ abcd+ ' > test
python
import os
output = os.execl('/usr/bin/sed', '-e', 's#\([[:alnum:]_+]\{1,\}\) #\\1/#g', 'test')

sed正規表現の外側の一重引用符を保持しながら、一重引用符を正規表現の一部として使用するには、単一引用符でsed囲まれた 3 つの個別の文字列を連結して、シェルの拡張を回避します。

# man bash:
# "A single quote may not occur between single quotes, even when preceded by a backslash."
# cf. http://stackoverflow.com/a/9114512 & http://unix.stackexchange.com/a/82757
# concatenate: 's/doesn'  +  \'  +  't/does not/'
echo "sed doesn't work for me" | sed -e 's/doesn'\''t/does not/'
于 2013-09-12T14:53:25.903 に答える