/bin/shは文字列を評価しますが、Bashはそうではありません。

/bin/shは文字列を評価しますが、Bashはそうではありません。

sh以下のスクリプトを実行すると、使用されているシェルが次のかどうかに応じて2つの異なる出力が表示されますbash

regex(){
     echo 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'
}

replace_builtins(){
    sed -e "$(regex)"
}

echo 'if !has(\"nvim\"): ' | replace_builtins
  • 強く打つ:if !MOCK_has(\"nvim\"):
  • シェン:??MOCK_has(\"nvim\"):

(この疑問符は元の端末からそのままコピーされましたが、投稿を保存すると消えました。デフォルトでは印刷できない文字です。)

この動作を説明するために、POSIX shモードで実行したときに何が起こるかを知りたいです。

編集:ボーナスポイントの場合、echo関数内で交換したときにBashでもこれが起こる理由を説明してください。printfregex

     printf 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'

答え1

説明はにあります。POSIX仕様echo:

標準出力に書き込まれる文字列。最初のオペランドがある-n場合、またはオペランドに<backslash>文字が含まれている場合、結果は実装に従って定義されます。

POSIXは主に歴史的慣行を性文化し、時には歴史的慣行は一貫していません。一部のシェルは、引数のエスケープシーケンスをたとえばタブとechoバイト\t\1が1()の文字に展開します^A。他のシェルはバックスラッシュをプレーン文字として扱います。

ランダムな文字列を印刷する移植可能な方法は、常に最初の引数(形式)でバックスラッシュエスケープシーケンスを拡張することを使用することですprintfprintf文字列を文字通り印刷するには、次のようにします。

printf %s 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'

文字列を文字通り印刷し、末尾に改行文字を追加するには、次のようにします。

printf '%s\n' 's/\(.* \)\(!\{0,1\}\)has(/\1\2MOCK_has(/g'

シェルスクリプトで一重引用符リテラルを使用して文字列を作成する場合は、一重引用符文字をで書く必要があります'\'''。これはシェル構文に関する質問で、文字通り文字列を印刷するのとはまったく異なる質問です。

答え2

興味深い質問を見つけました...

問題は、POSIX互換性が不足していることですdash

POSIX 小規模組み込みシステムと Linux など、UNIX 互換性を必要とする大規模システムを区別するデフォルトの POSIX 互換性レベル。後者の場合、システムはいわゆるXSI拡張をすべて実装する必要があります。

XSI互換システムには、echo拡張パラメータでいくつかのバックスラッシュエスケープが必要です。

bashbashPOSIX / XSI準拠の動作でコンパイルすることは可能ですが(たとえば、SolarisやMacOSで実行されます)、Linuxのバイナリはこれを実行しません。 POSIX / XSI準拠のためにコンパイルされている場合、パラメータのバックスラッシュエスケープをbash正しく処理し、サンプルコードエスケープシーケンスにPOSIX / XSIがないため、サンプルechoコードはSolarisまたはMacOSの対応するバイナリに対して機能します。bash

XSIはLinuxと互換性がないため、bashパラメータのバックラッシュエスケープをまったく拡張しません。これがサンプルコードがechoLinuxでも機能する理由です。bash

dash相手はPOSIX / XSI準拠を主張し、echoパラメータのバックスラッシュエスケープを拡張します。 POSIX / XSI準拠が正しく実装されると、サンプルコードdashも機能します。dashこれは、サンプルコードにPOSIX / XSIバックスラッシュエスケープシーケンスが含まれていないためです。

POSIX/XSI にはecho拡張が必要です。

\0nnn  for an octal number that represents the related character

サンプルコードにはバックスラッシュシーケンスが含まれています。

\1 for the first sed subexpression

そして

\2 for the second sed subexpression

echoそしてこれはPOSIX / XSIエスケープシーケンスの一部ではないため、POSIX互換シェルの組み込み関数はそれを拡張できません。ただし、POSIXでは8進数が誤って拡張されるため、これを禁止します。これがサンプルコードが失敗して表示される理由です。echodash\1\2dash

バグレポートを送信しdashて修正されるecho argのをprintf '%s\n' arg待つdashか。printfdash

したがって、POSIX / XSIエラーを一覧表示できますdash

  • マルチバイト文字はサポートされていません。

  • 禁止されていてもパラメータ\nnnで拡張されます。echo

  • これが必要な場合でも、\nnnパラメータは拡張されません。printf

関連情報