bashとzshの違い
| 対応: | 全Linux | |
|---|---|---|
| macOS(2001 Cheetah) | ||
| Bash 1.0(1989) |
macOS Catalina(2019年)以降、デフォルトシェルが『bash』から『zsh』に変更されました。両者の構文はほぼ同じですが、配列のインデックスやグロブの挙動など、知らずにハマるポイントがあります。このページではスクリプトを書くときに注意すべき違いをまとめます。
主な違い一覧
| 項目 | bash | zsh |
|---|---|---|
| 配列の開始インデックス | 0 始まり | 1 始まり |
| 変数の単語分割 | $var はスペースで自動分割 | $var は分割しない(安全) |
| グロブ(**) | shopt -s globstar が必要 | デフォルトで使える |
| マッチなしグロブ | パターン文字列がそのまま残る | エラーになる(nomatch) |
| プロンプト変数 | \u、\h、\w | %n、%m、%~ |
| オプション設定 | shopt -s オプション名 | setopt オプション名 |
| 設定ファイル | ~/.bashrc | ~/.zshrc |
| 補完システム | bash-completion(外部) | compinit / compsys(組み込み) |
| 右プロンプト | なし | RPROMPT で設定可能 |
| スペル修正 | なし | setopt correct で有効 |
サンプルコード
配列の開始インデックスが異なります。bash は 0 始まり、zsh は 1 始まりです。
users=("user1" "user2" "user3")
echo ${users[0]}
user1
echo ${users[1]}
user2
実行するコマンドは次の通りです。
users=("user1" "user2" "user3")
echo ${users[1]}
user1
echo ${users[2]}
user2
bash ではクォートなしの変数がスペースで自動分割されますが、zsh では分割されません。
msg="hello world" for word in $msg; do echo "[$word]"; done [hello] [world]
実行するコマンドは次の通りです。
msg="hello world" for word in $msg; do echo "[$word]"; done [hello world]
再帰グロブ(**)を使ってサブディレクトリのファイルを検索します。bash では事前に『shopt -s globstar』が必要です。
shopt -s globstar ls **/*.txt
実行するコマンドは次の通りです。
ls **/*.txt
グロブがどのファイルにもマッチしない場合の挙動が異なります。bash はパターン文字列をそのまま渡しますが、zsh はエラーを返します。
ls *.xyz ls: *.xyz: No such file or directory
実行するコマンドは次の通りです。
ls *.xyz zsh: no matches found: *.xyz
プロンプトの設定記法が異なります。
bash の場合(~/.bashrc)
PS1='\u@\h:\w\$ '
zsh の場合(~/.zshrc)
PS1='%n@%m:%~%# '
| 意味 | bash | zsh |
|---|---|---|
| ユーザー名 | \u | %n |
| ホスト名 | \h | %m |
| カレントディレクトリ | \w | %~ |
| プロンプト記号(root は #) | \$ | %# |
シェルオプションの設定方法が異なります。
shopt -s nocaseglob shopt -s globstar shopt -u nocaseglob
実行するコマンドは次の通りです。
setopt no_case_glob setopt glob_star_short unsetopt no_case_glob
よくあるミス
よくあるミス1: 配列インデックスの違いに気づかない
bash で書いたスクリプトを zsh で実行すると、配列の最初の要素の取り出し方が変わります。
bash の場合(インデックス 0 が最初)
names=("user1" "user2" "user3")
echo ${names[0]}
user1
zsh の場合(インデックス 0 は空)
names=("user1" "user2" "user3")
echo ${names[0]}
zsh でインデックス 0 を参照しても何も表示されません。最初の要素は ${names[1]} です。移植性を重視する場合はシェバンで bash を明示します。
よくあるミス2: グロブがマッチしないときの挙動の違い
bash ではマッチしないグロブパターンがそのまま文字列として渡されますが、zsh ではエラーになります。
bash の場合(パターン文字列がそのまま渡る)
rm *.xyz rm: *.xyz: No such file or directory
zsh の場合(エラーになる)
rm *.xyz zsh: no matches found: *.xyz
zsh では存在しないパターンを渡すとコマンド自体が実行されません。スクリプトの移植時に注意が必要です。
概要
シェルスクリプト(『#!/bin/bash』)として書いたファイルは bash の構文で実行されるため、普段 zsh を使っていてもスクリプトの動作には影響しません。違いが問題になるのはターミナルで直接コマンドを打つ場合や、『~/.bashrc』と『~/.zshrc』の設定を書く場合です。
最も注意すべきは配列の開始インデックスの違いです。bash は 0 始まり、zsh は 1 始まりのため、同じスクリプトでも結果が変わります。
シェバンの使い方は シェバン(shebang)/ chmod +x を、変数の展開については ${パラメータ展開} を、for 文のグロブ活用は for を参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。