📺

macOS の zsh ではこれだけはやっておこう

2021/09/11に公開

Homebrew を使います

https://brew.sh/

まだ Homebrew をインストールしていない場合はこちら:

shell
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

1. パスの設定

M プロセッサ (Apple Silicon) 搭載 Mac の場合は、Homebrew 向けにパスを通しておく必要があります。

# デフォルトのパスを確認
% echo $path
/usr/local/bin /usr/bin /bin /usr/sbin /sbin /Library/Apple/usr/bin /Library/Frameworks/Mono.framework/Versions/Current/Commands

Intel Mac であればこのままでも差し支えありませんが、Apple Silicon Mac では Homebrew のインストール先が /opt/homebrew 以下となるので、ここへパスを通しておきます。
ホームディレクトリ (/Users/ユーザー名) に .zshrc を作成します。

~/.zshrc
typeset -U path PATH
path=(
  /opt/homebrew/bin(N-/)
  /opt/homebrew/sbin(N-/)
  /usr/bin
  /usr/sbin
  /bin
  /sbin
  /usr/local/bin(N-/)
  /usr/local/sbin(N-/)
  /Library/Apple/usr/bin
)

優先してほしい順にパスを指定していきます。

(N-/) は、もしそのディレクトリが存在していれば PATH に追加し、そうでなければ無視してくれるオプションです。
上のパス設定を反映させるには ~/.zshrc を読み込みます。

shell
source ~/.zshrc

2. zsh-completions のインストール

コマンド入力にバシバシ補完を効かせてくれる zsh-completions をインストールします。

shell
brew install zsh-completions
~ 略 ~
=> Caveats
To activate these completions, add the following to your .zshrc:

  if type brew &>/dev/null; then
    FPATH=$(brew --prefix)/share/zsh-completions:$FPATH

    autoload -Uz compinit
    compinit
  fi

You may also need to force rebuild `zcompdump`:

  rm -f ~/.zcompdump; compinit

Additionally, if you receive "zsh compinit: insecure directories" warnings when attempting
to load these completions, you may need to run this:

  chmod -R go-w '/opt/homebrew/share/zsh'
~ 略 ~

インストール時の Caveats には、/opt/homebrew/share/zsh (Intel Mac の場合は /usr/local/share/zsh) のみを go-w せよと表示されていますが、実際には share ディレクトリそのものを chmod -R go-w する必要があります。

shell
chmod -R go-w /opt/homebrew/share

あとは Caveats の指示にしたがって、~/.zshrc への追記と補完キャッシュファイルの再生成をおこないます。

~/.zshrc
if type brew &>/dev/null; then
  FPATH=$(brew --prefix)/share/zsh-completions:$FPATH
  autoload -Uz compinit && compinit
fi
% source ~/.zshrc
% rm -f ~/.zcompdump; compinit

これでコマンド入力中に Tab キーまたは Ctrl+I を打鍵するとその後の候補を補完表示してくれます。

% sysctl _  # <-- ここで Tab または \C-I を入力

% sysctl _
audit     hw        kperf     machdep   security  vfs
debug     kern      ktrace    net       user      vm

% sysctl hw._ # <-- ここでふたたび Tab

% sysctl hw._
activecpu               cpufrequency            l3cachesize
busfrequency            cpufrequency_max        logicalcpu
busfrequency_max        cpufrequency_min        logicalcpu_max
busfrequency_min        cpusubfamily            memsize

3. zsh-autosuggestions のインストール

ターミナルのコマンド履歴に基づいてコマンド候補を表示、入力補完もしてくれる zsh-autosuggestions もインストールします。

shell
brew install zsh-autosuggestions
~ 略 ~
==> Caveats
To activate the autosuggestions, add the following at the end of your .zshrc:

  source /opt/homebrew/share/zsh-autosuggestions/zsh-autosuggestions.zsh

You will also need to force reload of your .zshrc:

  source ~/.zshrc
==> Analytics

~ 略 ~

Caveats にしたがって ~/.zshrc へ追記しましょう。

~/.zshrc
  if type brew &>/dev/null; then
    FPATH=$(brew --prefix)/share/zsh-completions:$FPATH
+   source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh
    autoload -Uz compinit && compinit
  fi
shell
source ~/.zshrc


この状態から Ctrl+E で補完をコンプリート

4. プロンプトの表示を変更

プロンプトのデフォルト値を確認します。

zsh
zenn@mba ~ $ echo $PROMPT
%n@%h %1~ %#

これらは以下のことを意味しています。

  • %n: ユーザー名
  • %h: ホスト名
  • %~: カレントディレクトリ(1 はディレクトリ深度)
  • %#: 一般ユーザーのときは %root になったときは # を表示

カレントディレクトリの表示形式にはいくつかのバリエーションがあります。

PROMPT 機能 実際の表示
%d フルパス /Users/zenn/Downloads/myapp:% _
%~ フルパス 2 ~/Downloads/myapp:% _
%c 相対パス myapp:% _
%2c ディレクトリ深度 2 Downloads/myapp:% _

ここでは次のように設定しました。

~/.zshrc
PROMPT="%n ($(arch)):%~"$'\n'"%# "
  • $(): カッコ内のコマンド結果を表示します
  • "$'\n'": プロンプトが長くなってしまったので途中に改行を挿入します
% source ~/.zshrc

zenn (arm64):~/Downloads/myapp
% _

https://zsh.sourceforge.io/Doc/Release/Prompt-Expansion.html

5. プロンプトへ色を付ける

zsh では簡単にプロンプトへ色をつけることができます。

まず、~/.zshrc の冒頭で色を扱うモジュールを有効化し、

~/.zshrc
autoload -Uz colors && colors

プロンプト内の文字列に色を指定します。

構文
%F{色の名前または色番号}hoge%f

%F ~ %f に挟まれた文字列 (hoge) に指定した色が付きます。

色の名前: black red green yellow blue magenta cyan white

~/.zshrc
PROMPT="%F{green}%n%f %F{cyan}($(arch))%f:%F{blue}%~%f"$'\n'"%# "

色番号を調べるには以下のスクリプトを実行してみてください。

palette.sh(クリックで展開トグル)
palette.sh
#!/bin/sh
#
# 256色のカラーパレットを表示する
#  bash と zsh にて実行可能
#

target_shell=$1

if [ -z "$1" ]; then
    target_shell=$(basename "$SHELL")
fi

if [ "$target_shell" = "bash" ]; then
    bash <<< 'for code in {0..255}; do echo -n "[38;05;${code}m $(printf %03d $code)"; [ $((${code} % 16)) -eq 15 ] && echo; done'
elif [ "$target_shell" = "zsh" ]; then
    zsh  <<< 'for code in {000..255}; do print -nP -- "%F{$code}$code %f"; [ $((${code} % 16)) -eq 15 ] && echo; done'
else
    echo "error: Invalid argument ($target)"
    echo "Usage: $0 [bash|zsh]"
fi

https://yonchu.hatenablog.com/entry/2012/10/20/044603

6. プロンプトへ Git リポジトリの状態を表示する

まず、zsh-git-prompt をインストールします。

shell
brew install zsh-git-prompt
~ 略 ~
==> Caveats
Make sure zsh-git-prompt is loaded from your .zshrc:
  source "/opt/homebrew/opt/zsh-git-prompt/zshrc.sh"
~ 略 ~

Caveats の指示に従い、~/.zshrc の中でスクリプトを読み込み、

~/.zshrc
source $(brew --prefix)/opt/zsh-git-prompt/zshrc.sh

プロンプトの中で git_super_status を展開するだけです。

~/.zshrc
PROMPT="%F{034}%h%f:%F{020}%~%f $(git_super_status)"$'\n'"%# "

Git リポジトリ以外では $(git_super_status) を表示させたくない場合

zsh には、bash における PROMPT_COMMAND と同等のフック関数 precmd() が用意されています。
precmd() フックへ、そのディレクトリが Git リポジトリかどうか判定する関数を追加します。

~/.zshrc
git_prompt() {
  if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = true ]; then
    PROMPT="%F{034}%h%f:%F{020}%~%f $(git_super_status)"$'\n'"%# "
  else
    PROMPT="%F{034}%h%f:%F{020}%~%f "$'\n'"%# "
  fi
}

precmd() {
  git_prompt
}

7. コマンド実行結果のあとに空行を挿入する

コマンドの実行結果が表示されたあと、すぐにプロンプトが表示されるのはやや窮屈です。

zenn (arm64):~/Downloads
% ls -a
.		..		.DS_Store	.localized	myapp
zenn (arm64):~/Downloads
% _

~/.zshrc へ以下を追記します。

~/.zshrc
add_newline() {
  if [[ -z $PS1_NEWLINE_LOGIN ]]; then
    PS1_NEWLINE_LOGIN=true
  else
    printf '\n'
  fi
}

precmd() {
  git_prompt
  add_newline
}

コマンド実行後に空行が挿入されるようになりました。

zenn (arm64):~/Downloads
% source ~/.zshrc

zenn (arm64):~/Downloads
% _

8. あたらしくインストールされたコマンドを即認識させる

デフォルト状態の zsh では、あたらしくインストールされたコマンドをただちに認識してはくれません。

~% brew install yarn

~% yarn --version
zshell: command not found: yarn

~/.zshrc の冒頭へ以下の行を追記しましょう。

~/.zshrc
zstyle ":completion:*:commands" rehash 1

9. tarball へ macOS の特殊ファイルを含めないようにする

macOS で作成した tar.gz を Windows などの他の OS で解凍すると、.DS_Store ファイルや ._ (ドットアンダーバー) ファイルなどの特殊ファイルが tarball に含まれてしまっていることがよくあります。
これを防ぐためのエイリアス関数を用意しましょう。

~/.zshrc
tgz() {
  env COPYFILE_DISABLE=1 tar zcvf "$1" --exclude=".DS_Store" "${@:2}"
}

使い方:

~% tgz dotfiles.tgz .zshrc .gitconfig .ssh/

a .zshrc
a .gitconfig
a .ssh
a .ssh/id_ed25519
a .ssh/id_ed25519.pub
a .ssh/config
a .ssh/known_hosts

~% ls
dotfiles.tgz

10. その他の小技集

1. rm * でいちいち確認しないで欲しい

~/.zshrc
setopt RM_STAR_SILENT

2. curl などで ?& をエスケープなしで使いたい

~/.zshrc
setopt nonomatch

3. ファイル名を補完した後に挿入されたスペースが消えてしまう

https://qiita.com/sprout2000/questions/daaa95192fab3ac291ee

~/.zshrc
ZLE_REMOVE_SUFFIX_CHARS=$''

ここまでの ~/.zshrc

(クリックで展開)
~/.zshrc
setopt nonomatch
setopt RM_STAR_SILENT
ZLE_REMOVE_SUFFIX_CHARS=$''

autoload -Uz colors && colors
zstyle ":completion:*:commands" rehash 1

if type brew &>/dev/null; then
  FPATH=$(brew --prefix)/share/zsh-completions:$FPATH
  autoload -Uz compinit && compinit
  source $(brew --prefix)/share/zsh-autosuggestions/zsh-autosuggestions.zsh
  source $(brew --prefix)/opt/zsh-git-prompt/zshrc.sh
fi

typeset -U path PATH
path=(
  /opt/homebrew/bin(N-/)
  /opt/homebrew/sbin(N-/)
  /usr/local/bin(N-/)
  /usr/local/sbin(N-/)
  /usr/bin
  /usr/sbin
  /bin
  /sbin
  /Library/Apple/usr/bin
)

if type brew &>/dev/null && type $(brew --prefix)/bin/python3.11 &>/dev/null; then
  alias python="$(brew --prefix)/bin/python3.11"
  alias pip="$(brew --prefix)/bin/pip3.11"
else
  alias python="/usr/bin/python3"
  alias pip="/usr/bin/pip3"
fi

git_prompt() {
  if [ "$(git rev-parse --is-inside-work-tree 2> /dev/null)" = true ]; then
    PROMPT="%F{034}%n%f:%F{020}%~%f $(git_super_status)"$'\n'"%# "
  else
    PROMPT="%F{034}%n%f:%F{020}%~%f "$'\n'"%# "
  fi
}

add_newline() {
  if [[ -z $PS1_NEWLINE_LOGIN ]]; then
    PS1_NEWLINE_LOGIN=true
  else
    printf '\n'
  fi
}

precmd() {
  git_prompt
  add_newline
}

tgz() {
  if [ $# -lt 2 ]; then
    echo "Usage: tgz DIST SOURCE"
  else
    xattr -rc "${@:2}" && \
    env COPYFILE_DISABLE=1 tar zcvf "$1" --exclude=".DS_Store" "${@:2}"
  fi
}

(おまけ) x64arm64 の環境を行ったり来たりする (Apple Silicon)

M プロセッサを搭載した Mac で Rosetta 2arm64 の環境をコマンドラインから切り替えられるようにします。

~/.zshrc
# エイリアスを設定
if (( $+commands[arch] )); then
  alias x64='exec arch -arch x86_64 "$SHELL"'
  alias a64='exec arch -arch arm64e "$SHELL"'
fi

# 上記エイリアスが実行されると環境変数を書き換えます
if [[ $(uname -m) == "x86_64" ]]; then
  export VOLTA_HOME="$HOME/.volta_x64"
  typeset -U path PATH
  path=(
    $VOLTA_HOME/bin
    /usr/local/bin(N-/)
    /usr/local/sbin(N-/)
    /usr/bin
    /usr/sbin
    /bin
    /sbin
    /Library/Apple/usr/bin
  )
else
  export VOLTA_HOME="$HOME/.volta"
  typeset -U path PATH
  path=(
    $VOLTA_HOME/bin
    /opt/homebrew/bin(N-/)
    /opt/homebrew/sbin(N-/)
    /usr/bin
    /usr/sbin
    /bin
    /sbin
    /usr/local/bin
    /usr/local/sbin
    /Library/Apple/usr/bin
  )
fi

2021-09-17-155039.png

Discussion