ダッシュのエコ拡張 \\\\ このbashのエコーと異なるのはなぜですか?

ダッシュのエコ拡張 \\\\ このbashのエコーと異なるのはなぜですか?

私はいくつかの理由でかなり移植性の高いシェルスクリプトで書こうとする小さなオープンソースプロジェクトを持っています。自動化された統合テストは、パス式の敵対文字が正しく処理されているかどうかを確認します。

/bin/shプロバイダが提供したユーザーを使用したbashテストでエラーが発生しました。これを次のように単純化しました。

echo "A bug\\'s life"
echo "A bug\\\\'s life"

Bash では、次のような予測結果が生成されます。

A bug\'s life
A bug\\'s life

私が開発したダッシュを使用して、次のことを行います。

A bug\'s life
A bug\'s life

まだダッシュでバグが見つからず、何かが抜けている可能性があります。これについて合理的な説明はありますか?

答え1

存在する

echo "A bug\\'s life"

二重引用符\で二重引用符内で特別なので、最初のものは\シェルで次のように理解されます。逃げる/引用する第二\。したがって、A bug\'s life引数がに渡されますecho

echo "A bug\'s life"

同じ効果を得るでしょう。'二重引用符の中には特別な内容がないため\削除されないため、渡されたパラメータとまったく同じですecho

説明どおりなぜprintfがechoより優れているのですか?echo、実装間には多くの違いがあります。

UNIX準拠の実装では、dash組み込みのecho改行文字、バックスペース文字、オクテット文字シーケンス...およびバックスラッシュ自体の\エスケープシーケンスを導入するために使用されます。\n\b\0123\\

一部(非POSIX)では、-eオプションが必要な場合、または適合モードでのみこれを実行します(たとえば、OS / Xなどの正しいオプションを使用してビルドするとき、またはbash環境でsh呼び出すときなど)。SHELLOPTS=xpg_echo

したがって、標準(Unix標準のみ、POSIXは動作を指定しません)echo

echo '\\'

それは次のとおりです。

echo "\\\\"

出力一つバックスラッシュ(bash一貫性モードでない場合):

echo '\\'

出力されます二つバックスラッシュ。

避けてecho使用するのが最善ですprintf

$ printf '%s\n' "A bug\'s life"
A bug\'s life

この場合、すべての実装で同じように機能しますprintf


dash1はこの点で互換性がありますが、UNIX仕様(POSIX + XSI)で必要なものは出力しません。echoecho -n-n<newline>

答え2

echoとprintfの問題は、バックティック文字が「特殊文字」の場合を理解することにのみ関連しています。

最も簡単なのは文字列を使用することですprintf '%s' "$string"
この場合、処理する必要がある特殊文字はなく、2番目の引数でprintfコマンドが受け取るすべての内容がそのまま印刷されます。

一重引用符のみが使用されることに注意してください。

$ printf '%s\n' '\\\\\\\\\T '       # nine \
\\\\\\\\\T                          # nine \

文字列が最初の引数として使用される場合、特定の文字は特殊です。
ペア\\は単一\\T単数を表しますT

$ printf '\\\\\\\\\T '              # nine \
\\\\T                               # four \

4つのペアはそれぞれ1つ\\に変換され\、最後の\Tペアは1つに変換されますT

$ printf '\\\\\\\\\a '              # nine \
\\\\                                # four \

4つのペアのそれぞれは\\単一の文字に変換され、\最後の文字は\aベル(BEL)文字(印刷できません)に変換されます。

でも同じ状況が発生します一部エコ実装。

ダッシュ実装は常に特殊なバックスラッシュ文字を変換します。

このコードをスクリプトに入れると:

set -- '\g ' '\\g ' '\\\g ' '\\\\g ' '\\\\\g ' '\\\\\\g ' '\\\\\\\g ' '\\\\\\\\g ' '\\\\\\\\\g '
for i ; do
    printf '<%-14s> \t<%-9s> \t<%-14s> \t<%-12s>\n' \
       "$(printf '%s ' "|$i|")" \
       "$(printf       "|$i|")" \
           "$(echo         "|$i|")" \
       "$(echo    -e   "|$i|")" ;
done

その後、ダッシュが印刷されます(dash ./script):

<|\g |         >        <|\g |    >     <|\g |         >        <-e |\g |    >
<|\\g |        >        <|\g |    >     <|\g |         >        <-e |\g |    >
<|\\\g |       >        <|\\g |   >     <|\\g |        >        <-e |\\g |   >
<|\\\\g |      >        <|\\g |   >     <|\\g |        >        <-e |\\g |   >
<|\\\\\g |     >        <|\\\g |  >     <|\\\g |       >        <-e |\\\g |  >
<|\\\\\\g |    >        <|\\\g |  >     <|\\\g |       >        <-e |\\\g |  >
<|\\\\\\\g |   >        <|\\\\g | >     <|\\\\g |      >        <-e |\\\\g | >
<|\\\\\\\\g |  >        <|\\\\g | >     <|\\\\g |      >        <-e |\\\\g | >
<|\\\\\\\\\g | >        <|\\\\\g |>     <|\\\\\g |     >        <-e |\\\\\g |>

最初の2つの列はすべてのシェル(printf)に対して同じです。
残りの2つは、使用されるエコーの特定の実装によって異なります。

例: ash ./script(Busybox グレー):

<|\g |         >        <|\g |    >     <|\g |         >        <|\g |       >
<|\\g |        >        <|\g |    >     <|\\g |        >        <|\g |       >
<|\\\g |       >        <|\\g |   >     <|\\\g |       >        <|\\g |      >
<|\\\\g |      >        <|\\g |   >     <|\\\\g |      >        <|\\g |      >
<|\\\\\g |     >        <|\\\g |  >     <|\\\\\g |     >        <|\\\g |     >
<|\\\\\\g |    >        <|\\\g |  >     <|\\\\\\g |    >        <|\\\g |     >
<|\\\\\\\g |   >        <|\\\\g | >     <|\\\\\\\g |   >        <|\\\\g |    >
<|\\\\\\\\g |  >        <|\\\\g | >     <|\\\\\\\\g |  >        <|\\\\g |    >
<|\\\\\\\\\g | >        <|\\\\\g |>     <|\\\\\\\\\g | >        <|\\\\\g |   >

使用されている文字がある場合はaダッシュを示します。

<|\a |         >        <| |     >      <| |          >         <-e | |     >
<|\\a |        >        <|\a |    >     <|\a |         >        <-e |\a |    >
<|\\\a |       >        <|\ |    >      <|\ |         >         <-e |\ |    >
<|\\\\a |      >        <|\\a |   >     <|\\a |        >        <-e |\\a |   >
<|\\\\\a |     >        <|\\ |   >      <|\\ |        >         <-e |\\ |   >
<|\\\\\\a |    >        <|\\\a |  >     <|\\\a |       >        <-e |\\\a |  >
<|\\\\\\\a |   >        <|\\\ |  >      <|\\\ |       >         <-e |\\\ |  >
<|\\\\\\\\a |  >        <|\\\\a | >     <|\\\\a |      >        <-e |\\\\a | >
<|\\\\\\\\\a | >        <|\\\\ | >      <|\\\\ |      >         <-e |\\\\ | >

バッシュの場合:

<|\a |         >        <| |     >      <|\a |         >        <| |        >
<|\\a |        >        <|\a |    >     <|\\a |        >        <|\a |       >
<|\\\a |       >        <|\ |    >      <|\\\a |       >        <|\ |       >
<|\\\\a |      >        <|\\a |   >     <|\\\\a |      >        <|\\a |      >
<|\\\\\a |     >        <|\\ |   >      <|\\\\\a |     >        <|\\ |      >
<|\\\\\\a |    >        <|\\\a |  >     <|\\\\\\a |    >        <|\\\a |     >
<|\\\\\\\a |   >        <|\\\ |  >      <|\\\\\\\a |   >        <|\\\ |     >
<|\\\\\\\\a |  >        <|\\\\a | >     <|\\\\\\\\a |  >        <|\\\\a |    >
<|\\\\\\\\\a | >        <|\\\\ | >      <|\\\\\\\\\a | >        <|\\\\ |    >

これを行うには、コマンドを実行するシェルが文字列でも機能できるという説明を追加する必要があります。

$ printf '%s\n' '\\\\T '
\\\\T
$ printf '%s\n' "\\\\T "
\\T

シェルは二重引用符内にバックスラッシュを使用して操作を実行します。

このコードを使用すると:

tab='   '
say(){ echo "$(printf '%s' "$a") $tab $(echo "$a") $tab $(echo -e "$a")"; }
a="one \a "         ; say
a="two \\a "        ; say
a="t33 \\\a "       ; say
a="f44 \\\\a "      ; say
a="f55 \\\\\a "     ; say
a="s66 \\\\\\a "    ; say
a="s77 \\\\\\\a "   ; say
a="e88 \\\\\\\\a "  ; say
a="n99 \\\\\\\\\a " ; say

両方の効果が追加され、次のような結果が得られます。

$ bash ./script
one \a           one \a          one  
two \a           two \a          two  
t33 \\a          t33 \\a         t33 \a 
f44 \\a          f44 \\a         f44 \a 
f55 \\\a         f55 \\\a        f55 \ 
s66 \\\a         s66 \\\a        s66 \ 
s77 \\\\a        s77 \\\\a       s77 \\a 
e88 \\\\a        e88 \\\\a       e88 \\a 
n99 \\\\\a       n99 \\\\\a      n99 \\ 

ダッシュの場合、状況はさらに深刻です。

$ dash ./script
one              one             -e one  
two              two             -e two  
t33 \a           t33             -e t33  
f44 \a           f44             -e f44  
f55 \            f55 \           -e f55 \ 
s66 \            s66 \           -e s66 \ 
s77 \\a          s77 \a          -e s77 \a 
e88 \\a          e88 \a          -e e88 \a 
n99 \\           n99 \           -e n99 \ 

関連情報