チルダ置換のbash, ksh93, zshの違い [sh]
シェルではコマンドや引数がチルダで始まるときに置換が行われる。
チルダの後に続く(スラッシュまでの)文字がユーザー名である場合、 そのユーザーのログインディレクトリに置換される。 たとえばrootのログインディレクトリが/rootのとき、次のようになる。
$ echo ~root
/root
この置換はクォートの内部では働かない。
$ echo "~root"
~root
ここまでの動作はbash, ksh93, zshで共通だ。
チルダに続くのが変数置換のとき、 bashではチルダ置換が行われないが、ksh93とzshでは行われる。
$ sudo bash -c 'echo ~$USER'
~root
$ sudo ksh -c 'echo ~$USER'
/root
$ sudo zsh -c 'echo ~$USER'
/root
チルダに続くのがクォートされた文字列のとき、 bashではチルダ置換が行われないが、ksh93とzshでは行われる。
$ bash -c 'echo ~"root"'
~root
$ ksh -c 'echo ~"root"'
/root
$ zsh -c 'echo ~"root"'
/root
$ bash -c 'echo ~"ro""ot"'
~root
$ ksh -c 'echo ~"ro""ot"'
/root
$ zsh -c 'echo ~"ro""ot"'
/root
チルダに続く文字列がエスケープされているとき、 bashではチルダ置換が行われないが、ksh93とzshでは行われる。
$ bash -c 'echo ~\r\o\o\t'
~root
$ ksh -c 'echo ~\r\o\o\t'
/root
$ zsh -c 'echo ~\r\o\o\t'
/root
チルダに続くのがコマンド置換のとき、3つのシェルのいずれでもチルダ置換が行われる。
$ bash -c "echo ~`echo root`"
/root
$ ksh -c "echo ~`echo root`"
/root
$ zsh -c "echo ~`echo root`"
/root
$ bash -c "echo ~$(echo root)"
/root
$ ksh -c "echo ~$(echo root)"
/root
$ zsh -c "echo ~$(echo root)"
/root
チルダ置換はコマンド、引数の先頭以外にも、変数代入の文脈で値をコロン区切りにした先頭部分でも行われる。
$ PATH=~root:~root
$ echo $PATH
/root:/root
しかし、どこが変数代入の文脈とみなされるかはbashとksh93, zshで異なるようだ。 ksh93とzshではコマンド前のA=Bやexportの文脈のみだが、bashではそれ以外でも変数代入の形をしていればチルダ置換をする。
$ bash -c 'echo PATH=~root:~root'
PATH=/root:/root
$ ksh -c 'echo PATH=~root:~root'
PATH=~root:~root
$ zsh -c 'echo PATH=~root:~root'
PATH=~root:~root
bashは上記のような無関係(?)な箇所でチルダ展開が起こることに注意しなければならない。
一方ksh93やzshでも PATH=... command
と env PATH=... command
でチルダ展開の有無が異なることに注意しなければならない。
(前者の...の箇所では起こるが、後者の...の箇所では起こらない)
ここまでの例ではksh93とzshの動作は同じだったが、存在しないユーザーが指定された場合の動作はbash, ksh93とzshで異なる。 zshの場合はエラーとなり、ほかの2つではチルダがそのまま残される。
$ bash -c 'echo ~nosuchuser'
~nosuchuser
$ ksh -c 'echo ~nosuchuser'
~nosuchuser
$ zsh -c 'echo ~nosuchuser'
zsh:1: no such user or named directory: nosuchuser
もう一つ変なものを見つけてしまった。 チルダプレフィクス(チルダからスラッシュが来るまで、またはコマンドや引数が終わるまで)がチルダ単体の場合は自身の$HOMEで置換されるが、 bashとksh93ではコロンでもよい。zshはそうではない。
# bash -c 'echo ~:~'
/root:~
# ksh -c 'echo ~:~'
/root:~
# zsh -c 'echo ~:~'
~:~
シェルのコマンド置換の内部でのコメント [sh]
シェルのパーサーを書こうとしていていたのだけどマニュアルから読み取れないところはどうしても動かして実地調査するしかない。 シェルのコマンド置換の内部にコメントを書いて、そのコメントの中にコマンド置換の終端記号が来た場合、終端記号が勝つのかコメントが勝つのかという問題を調べた。
つまりこういうのを試す。
echo $(echo hello # comment)
echo $(echo hello
# comment)
echo `echo hello # comment`
echo `echo hello
comment`
結果
$ echo $(echo hello # comment)
> )
hello
$ echo $(echo hello
> # comment)
> )
hello
$ echo `echo hello # comment`
hello
$ echo `echo hello
> # comment`
hello
つまりダラーの場合はコメントのほうが勝って、バッククォートの場合はバッククォートのほうが勝つ。 bashでもksh93でもzshでも同様の動き。
めんどくさい… 忘れないようにメモしておく。