変数の値にスペースが含まれているとき、zshとbashで単語の分割ルールが違う。zshでシェルスクリプトを書くときに注意が必要になるので紹介する。
例1: 変数をコマンドの引数に指定する
変数の値にスペースを入れて、それをtouchコマンドの引数にしてみる。
zshの場合は変数の値が1つの単語として扱われる。なのでfile1 file2
という1つのファイルが作成される。
% filename="file1 file2"
% touch $filename
% ls -l
-rw-r--r-- 1 mollifier mollifier 0 12月 4 10:36 file1 file2
bashやshの場合は変数の値が中に含まれるスペースで区切られる。なのでfile1
、file2
という2つのファイルが作成される。
# bashまたはshの場合
% filename="file1 file2"
% touch $filename
% ls -l
-rw-r--r-- 1 mollifier mollifier 0 12月 4 10:37 file1
-rw-r--r-- 1 mollifier mollifier 0 12月 4 10:37 file2
つまり、zshの場合はtouch "file1 file2"
、bashの場合はtouch "file1" "file2"
というふうに変数の値が展開される。
例2: コマンドを変数に代入して実行する
コマンドと引数を変数に代入して実行する場合も同じで、zshの場合は変数の値を1つのコマンドとして実行しようとする。
# zshの場合
% cmd="echo hoge"
% $cmd
zsh: command not found: echo hoge
bashやshの場合は変数の値を中に含まれるスペースで区切ってから実行する。
# bashまたはshの場合
% cmd="echo hoge"
% $cmd
hoge
zshでbashと同じようにスペースで区切る
zshでもbashと同じように変数の値を分割することもできる。方法は2つ。
方法1
SH_WORD_SPLIT
オプションを有効にすると変数を分割するルールがbashと同じになる。
setopt SH_WORD_SPLIT
% cmd="echo hoge"
% $cmd
hoge
方法2
変数を${=VAR}
という形式で参照すると、SH_WORD_SPLIT
オプションを有効にしたときと同じように変数の値が展開される。
% cmd="echo hoge"
% ${=cmd}
hoge
「方法1」はオプションを無効にするまでその設定になってしまうので、SH_WORD_SPLIT
オプションはデフォルト(無効)のままで必要があるときだけ「方法2」のやり方で展開するのがよいと思う。
たまに、この方法を知らないと思われる人がevalしてるのを見ることがある。
% cmd="echo hoge"
% eval $cmd
evalは無理矢理な感じがするので、${=cmd}
を使ってみてください!
bashでもzshと同じようにスペースで区切らないようにする
bashでも変数を"
で囲むと、zshと同じように変数の値が分割されないようになる。
# bashまたはshの場合
% filename="file1 file2"
% touch "$filename"
% ls -l
-rw-r--r-- 1 mollifier mollifier 0 12月 4 10:37 file1 file2
なので、bashまたはsh用のシェルスクリプトを書くときは、基本的には変数名を"
で囲っておいたほうが安全。