mattintosh note

どこかのエンジニアモドキの備忘録

sh・bash・zsh 他 echo 比較

Qiita に投稿しようと思ったけど書式エラーで貼れなかったのでこっちに貼っておく。

シェルおよびコマンドの種類

比較項目

  1. オプションフラグ
  2. タブ
  3. 改行
  4. 改行抑制
  5. エスケープ文字
  6. エスケープ文字(クオート展開)
  7. ユニコード指定
echo -n ${LINENO}
echo -e ${LINENO}
echo -E ${LINENO}

echo    "   "
echo    "\t"
echo -e "\t"

echo    "${LINENO}
${LINENO}"
echo    "${LINENO}\n${LINENO}"
echo -e "${LINENO}\n${LINENO}"

echo    "${LINENO}\c"
echo -e "${LINENO}\c"

echo    \\e[31m${LINENO}\\e[m
echo    \\033[31m${LINENO}\\033[m
echo    \\x1b[31m${LINENO}\\x1b[m
echo    "\e[31m${LINENO}\e[m"
echo    "\033[31m${LINENO}\033[m"
echo    "\x1b[31m${LINENO}\x1b[m"
echo -e \\e[31m${LINENO}\\e[m
echo -e \\033[31m${LINENO}\\033[m
echo -e \\x1b[31m${LINENO}\\x1b[m
echo -e "\e[31m${LINENO}\e[m"
echo -e "\033[31m${LINENO}\033[m"
echo -e "\x1b[31m${LINENO}\x1b[m"

echo [32m${LINENO}[m
echo "[32m${LINENO}[m"

echo $'\e[31m'${LINENO}$'\e[m'
echo $'\x1b[31m'${LINENO}$'\x1b[m'
echo $'\033[31m'${LINENO}$'\033[m'

                 echo \\uf8ff
LANG=ja_JP.eucJP echo \\uf8ff
LANG=ja_JP.UTF-8 echo \\uf8ff

比較表

環境によって結果が異なる可能性があります。元データはページ下部にあります。

○:正しく展開もしくは認識される
△:展開されるが一部問題がある
×:展開されない
?:マニュアル上では使用可能なはずなのに何故か展開されない?

/bin/sh /bin/bash /bin/bash
(xpg_echo)
/bin/zsh /bin/zsh
(bsdecho)
BSD ECHO
(/bin/echo)
GNU ECHO
オプションフラグ
-n
-e × ×
-E × ×
タブ
"<ht>"
"\t" × × × ×
-e "\t"    △ *1 ×
改行
"<nl>"
"\n" × × ×
-e "\n"    △ *1 ×
改行抑制
"\c" × × × ×
-e "\c"    △ *1    △ *1
エスケープ文字
\\e ×    × *2    × *2 × ×
\\x1b
\\033
×    × *2    × *2 × ×
"\e" × × × ×
"\x1b"
"\033"
× × × ×
-e \\e    ? *1    × *2    × *2 ×
-e \\x1b
-e \\033
   △ *1    × *2    × *2 ×
-e "\e"    ? *1 ×
-e "\x1b"
-e "\033"
   △ *1 ×
エスケープ文字(RAW)
<esc>    × *2    × *2
"<esc>"
クオート展開
$'\e'
$'\x1b'
$'\033'
ユニコード指定
\uXXXX - - -    ○ *3 - - -

*1:-e も一緒に出力される。 *2:文法エラーで終了ステータス 1。 *3:LANG 未設定や LANG=ja_JP.eucJP などに設定されている場合は使えない。

特徴

/bin/sh

  • デフォルトでは xpg-echo が有効。
  • オプションが一切無い。
  • \c による改行抑制が可能。
  • クオート内外で \033\x1b は展開されるが \e は展開されない。(???)

/bin/bash

  • デフォルトでは xpg-echo が無効。
  • オプションは neE
  • \c 単独で改行抑制は出来ないが -e と併用すれば可能。
  • xpg_echo を有効にした場合でも \e は展開されない。(???)

/bin/bash (xpg_echo)

  • \e が使えないことを除いて /bin/zsh とほぼ同じ。

/bin/zsh

  • \c による改行抑制が可能。
  • \e などを用いる場合はクオートが必要。(メタキー誤動作防止用?)
  • \u\Uユニコードでの指定が可能。(設定による)

/bin/zsh(bsdecho)

  • BSD ECHO よりは xpg_echo 無しの bash に近い。

BSD ECHO(/bin/echo)

  • 最も機能が少なく -n\c のみ使用可能。

GNU ECHO (MacPorts Coreutils 版)

  • /bin/bash とだいたい同じ。ただしこちらは -e\e が展開される。

全てのシェルで同じ出力をする

printf を使う

printf '\e[m31mfoobar\e[m\n'

メモ:Autoconf によって生成される configure なんかでは printf を使った as_echoas_echo_n 変数が設定されていたりする。

as_echo='printf %s\n'
as_echo_n='printf %s'

$as_echo "foobar"
$as_echo_n "foobar"

関数で /bin/zsh を経由する

初期化を無効にするオプションってあったっけ?

echo(){
    /bin/zsh -c "echo \"\${@}\"" -- "${@}"
}

printf を使わない場合

改行抑制

OS X の場合 /bin/echo を使うようにすれば -n オプションで改行が抑制できる。ただしビルトインに比べると遅い。

/bin/echo -n foo

エスケープ文字

$'string' による展開を用いる。

echo $'\e[31m'foobar$'\e[m'

テストスクリプトと出力サンプル

  • 出力結果を正しく表示するためにはページ幅が 188 以上必要です。
#!/bin/sh
#
# GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)
#

SH=/bin/sh
BASH=/bin/bash
ZSH=/bin/zsh
BSD_ECHO=/bin/echo
if   test -x "/opt/local/bin/gecho" ; then GNU_ECHO=/opt/local/bin/gecho
elif test -x "/usr/local/bin/gecho" ; then GNU_ECHO=/usr/local/bin/gecho
else GNU_ECHO=:
fi

#-------------------------------------------------------------------------------

e1()(
    /bin/cat <<\EOS
echo -n ${LINENO}
echo -e ${LINENO}
echo -E ${LINENO}
EOS
)
e2()(
    /bin/cat <<\EOS
echo    "   "
echo    "\t"
echo -e "\t"
EOS
)
e3()(
    /bin/cat <<\EOS
echo    "${LINENO}
${LINENO}"
echo    "${LINENO}\n${LINENO}"
echo -e "${LINENO}\n${LINENO}"
EOS
)
e4()(
    /bin/cat <<\EOS
echo    "${LINENO}\c"
echo -e "${LINENO}\c"
EOS
)
e5()(
    /bin/cat <<\EOS
echo    \\e[31m${LINENO}\\e[m
echo    \\033[31m${LINENO}\\033[m
echo    \\x1b[31m${LINENO}\\x1b[m
echo    "\e[31m${LINENO}\e[m"
echo    "\033[31m${LINENO}\033[m"
echo    "\x1b[31m${LINENO}\x1b[m"
echo -e \\e[31m${LINENO}\\e[m
echo -e \\033[31m${LINENO}\\033[m
echo -e \\x1b[31m${LINENO}\\x1b[m
echo -e "\e[31m${LINENO}\e[m"
echo -e "\033[31m${LINENO}\033[m"
echo -e "\x1b[31m${LINENO}\x1b[m"
EOS
)
e6()(
    /bin/cat <<\EOS
echo [31m${LINENO}[m
echo "[31m${LINENO}[m"
EOS
)
e7()(
    /bin/cat <<\EOS
echo $'\e[31m'${LINENO}$'\e[m'
echo $'\x1b[31m'${LINENO}$'\x1b[m'
echo $'\033[31m'${LINENO}$'\033[m'
EOS
)
e8()(
    /bin/cat <<\EOS
                 echo "\uf8ff"
LANG=ja_JP.eucJP echo "\uf8ff"
LANG=ja_JP.UTF-8 echo "\uf8ff"
EOS
)

#-------------------------------------------------------------------------------

C0=188
C1=34
C2=20
Hr=$(/usr/bin/jot -b = -s "" ${C0})
escape()(
    /usr/bin/sed -n "
        s/"$'\e'"\[31m//
        s/"$'\e'"\[m//
        s/"$'\t'"/        /
        l
    "
)
Sh()(
    echo "${Hr}\n${1}\n${Hr}" \
    | /usr/bin/fmt -cw ${C0} \
    | /usr/bin/lam -P -${C0} -
)
Ss()(
    (
        echo "${1}"
        /usr/bin/jot -b "-" -s "" ${C2}
    ) \
    | /usr/bin/fmt -cw ${C2}
)
#-------------------------------------------------------------------------------

for f in e{1..8}
do
    case ${f} in
    e1) Sh "OPTION" ;;
    e2) Sh "TAB" ;;
    e3) Sh "NEWLINE" ;;
    e4) Sh "SUPPRESS TRAILING NEWLINE" ;;
    e5) Sh "ESCAPE CHARACTER" ;;
    e6) Sh "ESCAPE CHARACTER (RAW)" ;;
    e7) Sh "QUOTE EXPAND" ;;
    e8) Sh "UNICODE" ;;
    esac

( Ss "${SH}" ; ${f} | exec -c ${SH} -s | escape ) \
| \
(
    ( Ss "${BASH}" ; ${f} | exec -c ${BASH} -s | escape ) \
    | \
    (
        ( Ss "${BASH} (xpg_echo)" ; ( printf "shopt -s xpg_echo;" ; ${f} ) | exec -c ${BASH} -s | escape ) \
        | \
        (
            ( Ss "${ZSH}" ; ${f} | exec -c ${ZSH} -s 2>&1 | escape ) \
            | \
            (
                ( Ss "${ZSH} (bsdecho)" ; ( printf "setopt -s BSDECHO;" ; ${f} ) | exec -c ${ZSH} -s 2>&1 | escape ) \
                | \
                (
                    ( Ss "BSD ECHO (${BSD_ECHO})" ; ${f} | /usr/bin/sed "s|^echo|${BSD_ECHO}|" | exec -c ${SH} -s | escape ) \
                    | \
                    (
                        ( Ss "GNU ECHO" ; ${f} | /usr/bin/sed "s|^echo|${GNU_ECHO}|" | exec -c ${SH} -s | escape ) \
                        | \
                        (
                            (
                                echo "COMMAND" | /usr/bin/fmt -cw ${C1}
                                /usr/bin/jot -b - -s "" ${C1}
                                ${f} \
                                | /usr/bin/sed "s/"$'\t'"/        /" \
                                | /bin/cat -v
                            ) \
                            | \
                            (
                                /usr/bin/lam \
                                    -P -${C1}.${C1} /dev/fd/0 \
                                    -P -${C2}.${C2} -S "  " /dev/fd/{3..9}
                            )
                        ) 9<&0
                    ) 8<&0
                ) 7<&0
            ) 6<&0
        ) 5<&0
    ) 4<&0
) 3<&0
echo
done

以下、出力サンプルですが Qiita だと正しく表示されてないかもしれません。

  • $ は改行文字を表します。
  • <tab> はスペース8個に変換されています。
  • COMMAND 列を除き展開された <esc>^[)と [31m[m は省略されています。
============================================================================================================================================================================================
                                                                                           OPTION                                                                                           
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo -n ${LINENO}                   -n 1$                 12$                   12$                   12$                   12$                   1-e 2$                12$                 
echo -e ${LINENO}                   -e 2$                 3$                    3$                    3$                    3$                    -E 3$                 3$                  
echo -E ${LINENO}                   -E 3$                                                                                                                                                   

============================================================================================================================================================================================
                                                                                             TAB                                                                                            
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo    "        "                          $                     $                     $                     $                     $                     $                     $           
echo    "\t"                                $             \t$                           $                     $             \t$                   \t$                   \t$                 
echo -e "\t"                        -e         $                  $                     $                     $                     $             -e \t$                        $           

============================================================================================================================================================================================
                                                                                           NEWLINE                                                                                          
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo    "${LINENO}                  2$                    2$                    2$                    1$                    1$                    2$                    2$                  
${LINENO}"                          2$                    2$                    2$                    1$                    1$                    2$                    2$                  
echo    "${LINENO}\n${LINENO}"      3$                    3\n3$                 3$                    3$                    3\n3$                 3\n3$                 3\n3$               
echo -e "${LINENO}\n${LINENO}"      3$                    4$                    3$                    3$                    4$                    -e 4\n4$              4$                  
                                    -e 4$                 4$                    4$                    4$                    4$                                          4$                  
                                    4$                                          4$                    4$                                                                                    

============================================================================================================================================================================================
                                                                                  SUPPRESS TRAILING NEWLINE                                                                                 
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo    "${LINENO}\c"               1-e 2$                1\c$                  12$                   12$                   1\c$                  1-e 2$                1\c$                
echo -e "${LINENO}\c"                                     2$                                                                2$                                          2$                  

============================================================================================================================================================================================
                                                                                      ESCAPE CHARACTER                                                                                      
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo    \\e[31m${LINENO}\\e[m       \e[31m1\e[m$          \e[31m1\e[m$          \e[31m1\e[m$          zsh: bad pattern: \e  zsh: bad pattern: \e  \e[31m1\e[m$          \e[31m1\e[m$        
echo    \\033[31m${LINENO}\\033[m   2$                    \033[31m2\033[m$      2$                    zsh: bad pattern: \0  zsh: bad pattern: \0  \033[31m2\033[m$      \033[31m2\033[m$    
echo    \\x1b[31m${LINENO}\\x1b[m   3$                    \x1b[31m3\x1b[m$      3$                    zsh: bad pattern: \x  zsh: bad pattern: \x  \x1b[31m3\x1b[m$      \x1b[31m3\x1b[m$    
echo    "\e[31m${LINENO}\e[m"       \e[31m4\e[m$          \e[31m4\e[m$          \e[31m4\e[m$          4$                    \e[31m4\e[m$          \e[31m4\e[m$          \e[31m4\e[m$        
echo    "\033[31m${LINENO}\033[m"   5$                    \033[31m5\033[m$      5$                    5$                    \033[31m5\033[m$      \033[31m5\033[m$      \033[31m5\033[m$    
echo    "\x1b[31m${LINENO}\x1b[m"   6$                    \x1b[31m6\x1b[m$      6$                    6$                    \x1b[31m6\x1b[m$      \x1b[31m6\x1b[m$      \x1b[31m6\x1b[m$    
echo -e \\e[31m${LINENO}\\e[m       -e \e[31m7\e[m$       \e[31m7\e[m$          \e[31m7\e[m$          zsh: bad pattern: \e  zsh: bad pattern: \e  -e \e[31m7\e[m$       7$                  
echo -e \\033[31m${LINENO}\\033[m   -e 8$                 8$                    8$                    zsh: bad pattern: \0  zsh: bad pattern: \0  -e \033[31m8\033[m$   8$                  
echo -e \\x1b[31m${LINENO}\\x1b[m   -e 9$                 9$                    9$                    zsh: bad pattern: \x  zsh: bad pattern: \x  -e \x1b[31m9\x1b[m$   9$                  
echo -e "\e[31m${LINENO}\e[m"       -e \e[31m10\e[m$      \e[31m10\e[m$         \e[31m10\e[m$         10$                   10$                   -e \e[31m10\e[m$      10$                 
echo -e "\033[31m${LINENO}\033[m"   -e 11$                11$                   11$                   11$                   11$                   -e \033[31m11\033[m$  11$                 
echo -e "\x1b[31m${LINENO}\x1b[m"   -e 12$                12$                   12$                   12$                   12$                   -e \x1b[31m12\x1b[m$  12$                 

============================================================================================================================================================================================
                                                                                   ESCAPE CHARACTER (RAW)                                                                                   
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo ^[[31m${LINENO}^[[m            1$                    1$                    1$                    zsh: bad pattern: ^[  zsh: bad pattern: ^[  1$                    1$                  
echo "^[[31m${LINENO}^[[m"          2$                    2$                    2$                    2$                    2$                    2$                    2$                  

============================================================================================================================================================================================
                                                                                        QUOTE EXPAND                                                                                        
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
echo $'\e[31m'${LINENO}$'\e[m'      1$                    1$                    1$                    1$                    1$                    1$                    1$                  
echo $'\x1b[31m'${LINENO}$'\x1b[m'  2$                    2$                    2$                    2$                    2$                    2$                    2$                  
echo $'\033[31m'${LINENO}$'\033[m'  3$                    3$                    3$                    3$                    3$                    3$                    3$                  

============================================================================================================================================================================================
                                                                                           UNICODE                                                                                          
============================================================================================================================================================================================
              COMMAND                      /bin/sh              /bin/bash       /bin/bash (xpg_echo)        /bin/zsh         /bin/zsh (bsdecho)   BSD ECHO (/bin/echo)        GNU ECHO      
----------------------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------  --------------------
                 echo "\uf8ff"      \uf8ff$               \uf8ff$               \uf8ff$               zsh: character not i  \uf8ff$               \uf8ff$               \uf8ff$             
LANG=ja_JP.eucJP echo "\uf8ff"      \uf8ff$               \uf8ff$               \uf8ff$               $                     \uf8ff$               \uf8ff$               \uf8ff$             
LANG=ja_JP.UTF-8 echo "\uf8ff"      \uf8ff$               \uf8ff$               \uf8ff$               zsh: character not i  \uf8ff$               \uf8ff$               \uf8ff$             
                                                                                                      $                                                                                     
                                                                                                      $