sed
音域(y///
)を使用してある文字セットを別の文字セットに置き換えたいと思います。
このプログラムを使用するだけでなく、これがうまくいくことを願っていますtr
。
$ echo '[]{}abc' | tr '[ab}' 'gefh'
g]{hefc
ただし、sedを使用して同じ操作を実行すると、次のエラーが表示されます。
$ echo '[]{}abc' | sed 'y/[ab}/gefh/'
sed: 1: "y/[ab}/gefh/": unbalanced brackets ([])
[
文字をエスケープする必要があると予想したので、これは意味があります。ただし、この状況を回避しようとすると、次のようなさまざまなエラーが発生します。
$ echo '[]{}abc' | sed 'y/\[ab}/gefh/'
sed: 1: "y/\[ab}/gefh/": transform strings are not the same length
現在の回避策は、(1)単に使用するtr
か、(2)エスケープされた文字と一致する以外は何もしない音域の右側に「ダミー文字」を挿入することです。
$ echo '[]{}abc' | sed 'y/\[ab}/_gefh/'
g]{hefc
しかし、これは不満足で疑わしいです。たとえば、\
入力文字列の内側にあっても非常に安全ではありません。
$ echo '[]{}abc\' | sed 'y/\[ab}/_gefh/'
g]{hefc_
エスケープ文字自体を翻訳の一部として考慮せずに、sedの翻訳で文字をエスケープする正しい方法は何ですか?
答え1
sed
macOSを使用しているとします(macOS用FreeBSDがどこから来たのかを確認していませんが、この問題をデフォルトで表示できる唯一のシステムですsed
)。
$ echo '[]{}abc' | sed 'y/[ab}/gefh/'
sed: 1: "y/[ab}/gefh/": unbalanced brackets ([])
$ echo '[]{}abc' | sed 'y/\[ab}/gefh/'
sed: 1: "y/\[ab}/gefh/": transform strings are not the same length
$ echo '[]{}abc' | sed 'y/\[ab}/\gefh/'
g]{hefc
だから、一つ解決策は
[
不均衡な括弧を避けるために、最初の文字列をエスケープします。- 2番目の文字列に「no-op」バックスラッシュを追加して、2つの文字列の長さを等しくします。
または、
両方の文字列を囲むこともできます
[...]
。これは、文字列のどこにあるか気にせずに機械的に実行できるため、これを処理する最も安全な方法です[
。$ echo '[]{}abc' | sed 'y/[[ab}]/[gefh]/' g]{hefc
sed
または、macOSにHomebrewまたはFreeBSDのパッケージシステムを介してGNUをインストールして使用してください。
sed
私はこれをこの実装のバグだと思います。
答え2
あなたがやっていることは正しい方法です。[
sed では常に通常の文字でなければなりませんy///
。これは、次の文字クラスのtr
一部になるのとは異なります。[
[:alpha:]
残念ながら、sedのいくつかの実装には、sedでバランスの取れた括弧を解析しようとするバグがあるようです。私はFreeBSD 11.2とBusyBox 1.30.1であなたが説明するバグを観察しました。
バックスラッシュの操作はトリッキーです。バックスラッシュ+文字の動作はそうではありません。基準文字が\
、n
または区切り文字でない場合。したがって、特定の実装でバグを解決するためにこれを使用できますが、生成されたコードは他の実装では機能しない可能性があります。
移植可能な回避策は、[
変更したくない他の文字に一時的に置き換えて、同じ置換から独自に変換することです]
。誤った解析を防ぐために、この文字は、またはで]
ない^
必要があります:
。交換を実行するときは、角かっことその間の内容を使用して文字列を整理する必要があります。 FreeBSDは代替が好きではありません[]
。これを解決する簡単な方法は、前に追加の文字を追加することです]
。たとえば、一時的B
な[
。
y/[B_]/B[_]/; y/Bab}/gefh/; y/[B_]/B[_]/