rcmdnk's blog
Last update

zsh最強シェル入門

環境的にBashが前提になってる所がほとんどなので Zshに完全移行するつもりはないんですが、 なんとなく色々書いてたスクリプトとかをZshでも使えるようにしてみようとしたら 結構面倒だったので取り敢えず詰まったところとかについてまとめておきたいと思います。

配列の番号

基本的な部分にして最大のやっかいもの。 Bashは通常0番から配列が詰められますが、Zshでは1番から詰められます。

  • Bashの場合
1
2
3
4
5
6
7
8
$ bash
$ arr=(a b c d)
$ for ((i=0; i<5; i++));do echo "arr[$i]=${arr[$i]}";done
arr[0]=a
arr[1]=b
arr[2]=c
arr[3]=d
arr[4]=
  • Zshの場合
1
2
3
4
5
6
7
8
$ zsh
$ arr=(a b c d)
$ for ((i=0; i<5; i++));do echo "arr[$i]=${arr[$i]}";done
arr[0]=
arr[1]=a
arr[2]=b
arr[3]=c
arr[4]=d

これを解消するためにはbashの場合にarr=("" "${arr[@]}")みたいにして 0番に無理やり追加して1つずらす、というのも手ですが、 Zshの方で、配列を0番からに出来るksharraysと言うオプションがあるので、そちらの方が 分かりやすいです。

スクリプトの中で、

1
2
3
4
## Fix array index for ZSH
if [ "$ZSH_NAME" = "zsh" ];then
  setopt localoptions ksharrays
fi

の様にしておけばOK。 (localoptionsでその関数内だけで有効)

追記: 2014/01/15

配列についての追記:

追記ここまで

追記: 2017/01/23

localoptionsを使わなくても、

1
[ -z "$ZSH_VERSION" ] || emulate -L ksh

と、emulateを使ってもその関数内で閉じる事ができます。 こちらは配列以外にも色々とkshに近づけます。

Z-Shell Frequently-Asked Questions

追記ここまで

追記: 2018/08/19

ksharraysなどを変更せずに関数を作ってBash/Zshでの 動作を統一することも出来ます。

追記ここまで

グロブ展開

上のecho時にも、Bashの場合は"を除いても大丈夫ですが、 Zshでは[$i]の部分をグロブ展開しようとしてそんなファイルは無い、と怒られます。 (ディレクトリにarr3とかのファイルがもしあるならそのまま表示されますが。。。)

Zshの方がこの辺り賢くやってくれる、という点ではあるんですが、 Bashから移行した際には少し戸惑います。

これに対しては

setopt nonomatch

nonomatchオプションを指定するとbash的な動作になります。

ただせっかくの機能なのでZsh使う場合には~/.zshrcには書かずに、スクリプトでも "で囲ったりして使い分ける方が良いかな、と。

たまに色々なとこで使われててどうしようもない時の最終手段的なオプション。

typeset/localでの配列の初期化

Bashだと関数内とかで

local dirs=(a b c)

的に、local宣言と同時に配列を初期化することが出来ますが、 Zshだと

zsh: number expected

の様なエラーが出ます。配列として宣言するために-aを使っても駄目。

これは仕方ないので

local -a dirs
dirs=(a b c)

の様に2段構えにしてあります。(オプションでなんとかなるのかもしれませんが)

readの引数

組み込みコマンドのreadに関して。

入力数を設定する引数はBashでは-nなんですが、 Zshでの-nはちょっと別の特別な用途で使われるため(man zshall等参照)、 Bashの-nと同様の引数は-kになります。

1
2
3
4
5
if [ "$ZSH_NAME" = "zsh" ];then
  read -s -k 1 c
else
  read -s -n 1 c
fi

こんな感じで使い分け。

readにReturnを渡した時

上みたいに1文字だけ入力、とした時、直ぐにReturnを押すと Bashの場合には入力無しでcは長さ0のNULL状態になります。

一方、Zshの場合には改行?が入力される様です(文字数1)。

改行コードとかで対応出来るのかもしれませんが、 差し当たりこんな感じでcaseとかを使うと BashでもZshでもReturnを使うことが出来ます。

1
2
3
4
5
6
7
8
9
10
11
12
    case $c in
      "a")
        echo "input a"
        ;;
      ""|"
")
        echo "inptut Return"
        ;;
      *)
        echo "input Others"
        ;;
    esac

(ダブル)クォート

クォート関連で少し戸惑う所があったので。

for文等を回すときに

max=5
for ((i=0; i<max; i++));do echo $i;done

こんな感じで書こうとする時、Bashでは上の様に書いても大丈夫で、 さらに$max"max""$max"としてダブルクォートで囲ったりしても大丈夫。

一方、Zshの時はmax$maxのみが有効で "$max"を使うと

zsh: bad math expression: operand expected at `"5"'

となり5が数値でなく文字列として扱われておかしくなるようです。 "max"とすると

zsh: bad math expression: operand expected at `"max"'

となりダメです。

この他にも変数展開の点でZshはより色々できるが為に 動きが違うことがあるようです。

ShellScript - シェルスクリプト(sh/bash/zsh)で変数から変数へ代入する方法について - Qiita [キータ]

command/builtin

追記: 2013/12/27

command/builtin 追加。

Bashの場合、command cdなどとすると、仮にcdコマンドをaliasしていたり 関数で再定義していてもbuiltinの元のcdを使います。 builtin cdとしても同じ。

これがZshの場合、buildin cdは同様の動きをしますが、 command cdはbuiltinコマンドを見に行かず、 もし再定義してなければ

zsh: command not found: cd

となります。

command [-pVv] command [arg ...]

Run command with args suppressing the normal shell function lookup. Only builtin commands or commands found in the PATH are executed. If the -p option is given, the search for command is performed using a default value for PATH that is guaranteed to find all of the standard utilities. If either the -V or -v option is supplied, a description of command is printed. The -v option causes a single word indicating the command or file name used to invoke command to be dis- played; the -V option produces a more verbose description. If the -V or -v option is supplied, the exit status is 0 if command was found, and 1 if not. If neither option is supplied and an error occurred or command cannot be found, the exit status is 127. Otherwise, the exit status of the command builtin is the exit status of command.

man bash

command [ -pvV ]

The command word is taken to be the name of an external command, rather than a shell function or builtin. If the POSIX_BUILTINS option is set, builtins will also be executed but certain special properties of them are suppressed. The -p flag causes a default path to be searched instead of that in $path. With the -v flag, command is similar to whence and with -V, it is equivalent to whence -v.

man zshall

追記ここまで

まとめ

以下のサイトではBash/Zshに加え、Fish等も含め複数のシェルについて 幅広く比較してあります。 Bash/Zshだけ見ようと思うとちょっと冗長ですがかなり網羅してある感があります。

Unix Shells: Bash, Fish, Ksh, Tcsh, Zsh - Hyperpolyglot

一応上のテストはMacで以下のバージョンを使って行いました。

  • bash: 3.2.51(1)-release
  • zsh: 5.0.2
Sponsored Links
Sponsored Links

« OctopressのRSSを部分配信にする ターミナルでのディレクトリ移動を保存、取り出しする #screen »

}