Next: Assignments, Previous: File System Conventions, Up: Portable Shell
persistent urban legendとは反対に,Bourneシェルは変数とバッククオートさ
れている式が整然と分かれておらず,特に右側の割り当てとcase
の引数
がそうです.例えば,以下のコードを考えます.
case "$given_srcdir" in .) top_srcdir="`echo "$dots" | sed 's,/$,,'`" *) top_srcdir="$dots$given_srcdir" ;; esac
以下のように書くと,より読みやすくなります.
case $given_srcdir in .) top_srcdir=`echo "$dots" | sed 's,/$,,'` *) top_srcdir=$dots$given_srcdir ;; esac
そして,実際それはより移植性が高くなります.最初の試みの最初の
caseで,全てのシェルが"`..."..."...`"
を正しく解釈する
わけではないので,top_srcdir
の計算結果は移植性が高くありません.
更に悪いことには,同様に"`...\"...\"...`"
を全てのシェ
ルが解釈するわけではありません.二重引用符でバッククオートされている式の
内部で,二重引用符で囲まれた文字列を使用するための移植性を高める方法は全
くありません(pfew!).
$@
この移植性の問題を回避する伝統的な方法は,‘${1+"$@"}’を使用する ことです.残念ながら,この手法はMac OS Xでも使用されている,Zsh (3.x と 4.x)では動作しません.Bourneシェルをエミュレートしているとき,Zshは ‘${1+"$@"}’で単語の分離を実行します.
zsh $ emulate sh zsh $ for i in "$@"; do echo $i; done Hello World ! zsh $ for i in ${1+"$@"}; do echo $i; done Hello World !
Zshは,プレーンの‘"$@"’をおそらく処理しますが,上記の移植性の問題 のため,プレーンの‘"$@"’を使用することはできません.回避する方法の 一つは,‘${1+"$@"}’を‘"$@"’に変換するZshの“global aliases”に依存します.
test "${ZSH_VERSION+set}" = set && alias -g '${1+"$@"}'='"$@"'
より保守的な回避方法は,位置に依存する引数を用いなくても良い限り, ‘"$@"’を避けることです.例えば,以下の代わりを考えます.
cat conftest.c "$@"
この代わりに以下を使用することが可能です.
case $# in 0) cat conftest.c;; *) cat conftest.c "$@";; esac
${
var:-
value}
sh
を含め,古いBSDシェルはシェルの代入に対してコ
ロンを受け入れず,文句を言って終了します.
${
var=
literal}
: ${var='Some words'}
それ以外のDigital Unix V 5.0のようなシェルでは,“bad substitution”のた めに終了します.
Solarisの/bin/shにはこの解釈に恐ろしいバグがあります.変数を ‘}’を含む文字列に設定する必要があることを想像してください.この ‘}’文字で,影響ある変数が既に設定されているとき,Solarisの /bin/shは混乱します.このバグは,以下のように実行することで作 動されるはずです.
$ unset foo $ foo=${foo='}'} $ echo $foo } $ foo=${foo='}' # no error; this hints to what the bug is $ echo $foo } $ foo=${foo='}'} $ echo $foo }} ^ ugh!
‘}’は,シングル引用符で囲まれている場合でも,‘${’に一致する
ものとして解釈されているようです.二重引用符を使用すると問題は生じません.
${
var=
expanded-value}
default="yu,yaa" : ${var="$default"}
それはvarを‘M-yM-uM-,M-yM-aM-a’に設定し,すなわち,全ての文字 の八番目のビットがセットされるでしょう.‘$var’を展開するとき,シェ ルが八番目のビットを明示的にリセットするので,単純に‘echo $var’を使 用している現象が分かりません.このシェルにその違反で混乱させる二つの方法 は,以下のようになります.
$ cat -v <<EOF $var EOF
それと以下です.
$ set | grep '^var=' | cat -v
このバグの古典的で典型的なものの一つは以下のものです.
default="a b c" : ${list="$default"} for c in $list; do echo $c done
単一行に‘a b c’を得るでしょう.なぜでしょうか?それは,‘$list’ にスペースが無いためです.‘M- ’,すなわち八ビット目を設定するスペー スがあるので,IFSによる分離が実行されないのです!!!
良いニュースの一つは,Ultrixが‘: ${list=$default}’で正確に動作す ることです.すなわち,引用符で囲まない場合です.悪いニュースとし ては,QNX 4.25は,listをdefaultの最後の項目 に設定することです!
移植性の高い方法は,Ultrixで八番目のビットを二回切替えるために,二重(引 用符による)代入を使用することです.
list=${list="$default"}
...しかし,Solarisの‘}’のバグ(上記を参照してください)には用心 してください.安全にするには,以下を使用してください.
test "${var+set}" = set || var={value}
`
commands`
例えば,cdが何も出力しないことを調査したい場合,以下のことが生 じるかもしれないので,‘test -z "`cd /`"’を使用しないでください.
$ pwd /tmp $ test -z "`cd /`" && pwd /
‘foo=`exit 1`’の結果は,読者への演習問題として残しておきます.
$(
commands)
$ showrev -c /bin/sh | grep version Command version: SunOS 5.8 Generic 109324-02 February 2001 $ echo $(echo blah) syntax error: `(' unexpected
また,irix 6.5のBourneシェルもサポートされていません.
$ uname -a IRIX firebird-image 6.5 07151432 IP22 $ echo $(echo blah) $(echo blah)
‘$(commands)’を使用する場合,異なる表記方法 ‘$((expression))’は現在のシェルではコマンドではなく数式だと勘 違いするので,コマンドがカッコで始まらないように確かめて下さい.この勘違 いを避けるため,二つの開カッコの間にはスペースを挿入して下さい.