zsh関連の設定(2)【自分のPCで使用しているソフトウェア,設定の紹介 #6】
前回に引き続き,zshの設定ファイルについて,紹介していきたいと思います。
~/.zsh/functions
以下に読み込むファイルを分けて保存しておいて,必要なタイミングで読み込まれるようにします。
~/.zsh/functions ├── alias.zsh ├── bindkey.zsh ├── colorize.zsh ├── command-not-found.zsh ├── completion.zsh ├── confirm.zsh ├── environment.zsh ├── extract.zsh ├── git.zsh ├── history.zsh ├── less.zsh ├── longrun-command-tracker.zsh ├── ls.zsh ├── man.zsh ├── peco-history.zsh ├── prompt.zsh ├── ranger.zsh ├── shtest.zsh ├── termsupport.zsh ├── tmux.zsh ├── zgen.zsh └── zplug.zsh
environment.zsh
, completion.zsh
, zgen.zsh
(zplug.zsh
)は先に読み込まれているので,~/.zshrc
の後ろの方に以下のように書いておいて,列挙したファイルを読み込みます。
# Load custom plugins while read myfunction; do source "$ZSH_ROOT/functions/${myfunction}.zsh" done < <(cat << EOF alias history colorize command-not-found confirm extract git less ls man ranger shtest peco-history bindkey termsupport prompt EOF ) unset -v myfunction
先に読み込まれるべきファイルと分けておいて,もっと記述を簡単にすることも出来るのだけれど,ここで書く順番に読み込まれるので,細かい調整がしやすいという利点があります。
以下ではそれぞれのファイルの内容について,ざっと見ていこうと思います。何か役にたてば幸いです。
alias
エイリアスの設定を行います。コマンドが存在するかどうかで振る舞いを変えるようにしてあり,移植性を意識しています。
apt
系g
forgit
vim
fornvim
v
forvim
(nvim
)r
forranger
f
forthefuck
:q
forexit
- 癖で打ってしまうことが多いということに気づいたので...
- かなりシームレスな感覚になれます。
cf-*
- 指定した設定ファイルを編集するコマンド
- 指定されたものがディレクトリの場合
ranger
で該当ディレクトリを開く - 設定ファイルが細分化されているものはこちらの方が便利
- 指定されたものがディレクトリの場合
- 新しくソフトウェアをインストールしたらまずここに登録する
- どこにあるか覚える必要なし
- 指定した設定ファイルを編集するコマンド
rl-*
- 設定ファイルを再読み込みするコマンドを設定
- コマンドを覚える必要なし
- 設定ファイルを再読み込みするコマンドを設定
_ec
関数の第3引数以降には任意のコマンドを第2引数に対して実行できるようにしていますが,特に良い使い道は思い浮かびません。
# apt if hash apt-fast 2>/dev/null; then alias apt-upd='apt-fast update' alias apt-upg='apt-fast upgrade' alias apt-ins='apt-fast install' alias apt-get='apt-fast' else alias apt-upd='sudo apt update' alias apt-upg='sudo apt upgrade' alias apt-ins='sudo apt install' fi # git alias to "g" alias g='git' if hash nvim 2>/dev/null; then alias vim='nvim' alias vimdiff='nvim -d' fi alias v='vim' alias r='ranger' # I often type ":q" to exit terminal alias :q='exit' # s command alias o='s' alias info='info --vi-keys' # thefuck (https://github.com/nvbn/thefuck) alias f='eval "$(thefuck $(fc -ln -1 | tail -n 1)); fc -R"' alias -g ...='../..' alias -g ....='../../..' alias -g .....='../../../..' alias -g ......='../../../../..' function _speedometer() { speedometer -b -rx "$1" -tx "$1" } alias spdmeter='_speedometer' # enable aliases in sudo alias sudo='sudo ' # edit configuration file by $EDITOR (vim). function _ec() { local cmd alis if [[ ! -e "$2" ]]; then alias "cf-$1::NEW"="echo \"File '$2' doesn't exist. \"; \ confirm y \"Create new one?\" && $EDITOR '$2'" return -1 fi alis="$1" if [[ -n "$3" ]]; then local target target="$2" cmd="$3" shift 3 cmd="$cmd '$target' "$@"" elif [[ -f "$2" ]]; then cmd="$EDITOR '$2'" elif [[ -d "$2" ]]; then cmd="ranger --cmd='cd '$2''" # cmd="builtin cd '$2'; ls" fi alias "cf-${alis}"="$cmd" return 0 } _ec alias ${ZSH_ROOT}/functions/alias.zsh _ec completion ${ZSH_ROOT}/completions _ec compton ~/.config/compton/compton.conf _ec dotlink ~/.dotfiles/dotlink _ec dotrc ~/.dotfiles/dotrc _ec env ${ZSH_ROOT}/functions/environment.zsh _ec functions ${ZSH_ROOT}/functions _ec history ${ZSH_ROOT}/history _ec latexmk ~/.latexmkrc _ec luakit ~/.config/luakit _ec mplayer ~/.mplayer/config _ec mpd ~/.config/mpd/mpd.conf _ec mpv ~/.config/mpv/mpv.conf _ec mutt ~/.mutt/muttrc _ec nvim ~/.config/nvim/init.vim _ec ncmpcpp ~/.ncmpcpp/config _ec plug ~/.config/nvim/plug.vim _ec prompt ${ZSH_ROOT}/functions/prompt.zsh _ec ranger ~/.config/ranger/rc.conf _ec ranger.d ~/.config/ranger _ec s ~/bin/s_provider _ec tig ~/.tigrc _ec tmux ~/.tmux.conf _ec turses ~/.turses/config _ec vim ~/.vimrc _ec vimcolor ~/gitrepo/easy-reading.vim/colors/easy-reading.vim _ec vimperator ~/.vimperatorrc _ec w3m ~/.w3m/config _ec w3m-keymap ~/.w3m/keymap _ec websearch ~/Workspace/python/web_search/websearch/config.py _ec xdefaults ~/.Xdefaults _ec xmodmap ~/.Xmodmap _ec xmonad ~/.xmonad/xmonad.hs _ec xresources ~/.Xresources _ec zshrc ~/.zshrc _ec zgen ${ZSH_ROOT}/functions/zgen.zsh unfunction _ec # reload configurations rl-xdefaults() { xrdb ~/.Xdefaults ;} rl-xmodmap() { xmodmap ~/.Xmodmap ;} rl-xresources() { xrdb -load ~/.Xresources ;} rl-zshrc() { exec zsh -l ;}
bindkey
キーバインドを設定します。参照される関数などが定義されている必要があるので,読み込むのは後ろのほうで。
^xe
: コマンドラインを$EDITOR
で編集する。便利機能 デフォルトでは^x^e
に設定してあると思うので,あまり意味はないShift + Tab
: 補完でTab
と逆方向に進む。割と便利^p
/^n
: 履歴検索時,vimっぽく動くようにする^t
: fzf補完。cdするときや,ファイルがたくさんあるディレクトリでの補完など,あると便利。^r
:peco-select-history
と名はついているが,実際はfzfを使った履歴サーチ。よく紹介されてる奴。
bindkey -e # edit command line in vim: # by pressing: ^xe or ^i # Edit the current command line in $EDITOR autoload -U edit-command-line zle -N edit-command-line bindkey '^xe' edit-command-line # reverse-menu-complete by `Shift+Tab` bindkey '^[[Z' reverse-menu-complete # by C-p, C-n bindkey "^P" history-beginning-search-backward-end bindkey "^N" history-beginning-search-forward-end if hash fzf 2>/dev/null; then bindkey '^T' fzf-completion bindkey '^I' $fzf_default_completion fi bindkey '^r' peco-select-history
colorize
色が付けられるコマンドでは色を付けるようにする。
alias.zsh
と統合しても良いとは思うが,目的は別。
mplayer
twitter
diff
forcolordiff
tree
grep
autoload -U colors && colors ## Alias # mplayer alias alias mplayer='mplayer -msgcolor' # twitter color alias twitter='twitter -f ansi' # colordiff if hash colordiff 2>/dev/null; then alias diff='colordiff -u' else alias diff='diff -u' fi alias tree='tree -C' alias grep="grep --color=auto --exclude-dir={.bzr,CVS,.git,.hg,.svn}"
command-not-found
bashを使っていると,打ったコマンドが見つからなかった時,類似する名前のパッケージをサジェストしてくれるが,その機能をzshでも使うことが出来ます(但しUbuntuでのみ)。
if [[ -x /usr/lib/command-not-found ]] ; then function command_not_found_handler() { /usr/lib/command-not-found --no-failure-msg -- "$1" } fi
confirm
確認メッセージを出し,(y/n)
で答えさせて,その結果を利用できるようにした関数です。
他のスクリプト中で使用しています。
その他,特徴としては
- デフォルトの答えを予め設定できる(デフォルトの方は大文字で表示される)
- メッセージを設定できる
- 答えが正しく入力されないときは無限ループ
- 大文字小文字は無視
function confirm() { # confirm [ y | n ] [<message>] local yn mes __confirm ret _b _r _b="\e[1m" _r="\e[m" if [ "$1" = "y" ]; then yn="${_b}Y${_r}/${_b}n${_r}" mes="$2" ret=0 elif [ "$1" = "n" ]; then yn="${_b}y${_r}/${_b}N${_r}" mes="$2" ret=1 else yn="y/n" yn="${_b}y${_r}/${_b}n${_r}" mes="$1" ret=-1 fi echo -n "$mes ($yn) " read __confirm __confirm=$(echo ${__confirm} | tr '[:upper:]' '[:lower:]') case ${__confirm} in y|yes) return 0 ;; n|no) return 1 ;; "") if [ $ret -eq -1 ]; then echo "Please answer with 'y' or 'n'." confirm "$@" else return $ret fi ;; *) echo "Please answer with 'y' or 'n'." confirm $@;; esac }
extract
圧縮ファイルの展開を,拡張子によって判別して適切なコマンドで行う関数extract
対応拡張子:
tar.gz, tgz, tar.xz, zip, lzh, tar.bz2, tbz, tar.Z, gz, bz2, Z, tar, arj
実はあまり使うことのない機能だが,多分いつか役に立つときは来ると思います。
function extract() { case $1 in *.tar.gz|*.tgz) tar xzvf $1;; *.tar.xz) tar Jxvf $1;; *.zip) unzip $1;; *.lzh) lha e $1;; *.tar.bz2|*.tbz) tar xjvf $1;; *.tar.Z) tar zxvf $1;; *.gz) gzip -d $1;; *.bz2) bzip2 -dc $1;; *.Z) uncompress $1;; *.tar) tar xvf $1;; *.arj) unarj $1;; esac } alias -s {qz,tgz,zip,lzh,bz2,tbz,Z,tar,arj,xz}=extract
git
oh-my-zshのplugi以下にあるgit.zshを少し変更して使っています。長いので載せませんが,カレントディレクトリにコミットしていない変更があることを検出するなどの関数が含まれています。 プロンプトの生成時に使用しています。
history
履歴関連の設定です。
- ファイルに保存する履歴の量を増やす
- 重複した履歴は削除
- 端末ごとの履歴を逐次共有
HISTFILE="$ZSH_ROOT/history" HISTSIZE=10000 SAVEHIST=10000 # Show history case $HIST_STAMPS in "mm/dd/yyyy") alias history='fc -fl 1' ;; "dd.mm.yyyy") alias history='fc -El 1' ;; "yyyy-mm-dd") alias history='fc -il 1' ;; *) alias history='fc -l 1' ;; esac setopt extended_history # コマンドの開始時刻と経過時間を登録 setopt hist_ignore_dups # 直線のコマンド行と同一なら登録しない setopt hist_ignore_all_dups # 登録済みコマンドの古い方を削除 setopt hist_ignore_space # コマンド行の先頭が空白なら登録しない setopt hist_reduce_blanks # 余分な空白は詰めて登録 setopt hist_verify setopt share_history # ヒストリの共有
less
less
コマンドのオプションを設定
- カラー文字を表示できるように
- 大文字,下線文字に色を付けて表示
- ステータスラインを冗長表示(デフォルトのファイル名表示)
あとで紹介するman.zsh
と組み合わせてかなり便利になりました。
# less options: export LESS='-iMRj.5' # colored less export LESS_TERMCAP_mb=$(tput bold) # begin blinking export LESS_TERMCAP_md=$(tput bold; tput setaf 4) # begin bold (blue) export LESS_TERMCAP_me=$(tput sgr0) # end mode export LESS_TERMCAP_se=$(tput sgr0) # end standout-mode export LESS_TERMCAP_so=$(tput setaf 3; tput rev) # begin standout-mode (yellow) export LESS_TERMCAP_ue=$(tput rmul; tput sgr0) # end underline export LESS_TERMCAP_us=$(tput smul; tput setaf 2) # begin underline (green)
less
ls
コマンドにオプションを指定。
オプション | 説明 |
---|---|
--human-readable ,-h |
K,M,Gなど読みやすい単位を付けて表示(with -l ) |
--classify ,-F |
@ (シンボリックリンク),/ などを表示 |
--sort=version ,-v |
1.1 の. などを数字として扱ってソートする(See: info coreutils 'ls invocation' - 10.1.4 Details about version sort) |
--time-style=long-iso |
タイムスタンプのフォーマットを"%Y-%m-%d %H:%M"に |
--group-directories-first |
ディレクトリをはじめに表示する |
--color |
カラー表示する |
LS_OPTIONS="-hFv --time-style=long-iso --group-directories-first" if command -p ls --color -d . &>/dev/null 2>&1; then LS_OPTIONS="${LS_OPTIONS} --color" fi alias ls="ls ${LS_OPTIONS}"
man
man
コマンドをラップする関数。
引数のコマンドの種類によって,例えばビルトインコマンドであればzshbuiltins
のmanページの中でそのコマンドを検索して表示します。
ほんとちょっとしたことだけど,man
コマンドをより意識して使うようになりました。
また,同名の別の種類のコマンドが存在するときには,$PERCOL
(=fzf
)を使って選択できるようにしています。
あと,less
コマンドに結果を渡して見ているので,lessで色付きで見れるようになっていれば,manページも色付きで表示できます。
man zshbuiltins
を実行できるようにするためには,以下のコマンドを実行します。
$ mkdir -p ~/Downloads/zsh-doc; cd $_ $ wget http://downloads.sourceforge.net/project/zsh/zsh/5.0.2/zsh-5.0.2.tar.bz2 $ tar -xvf zsh-5.0.2.tar.bz2 $ sudo cp zsh-5.0.2/Doc/*.1 /usr/local/share/man/man1
また$MANLANG
という環境変数が$LANG
を一時的に上書きするようにしているので,これを日本語に設定しておけば,日本語のmanページが準備されているもの(あまり多くはありませんが)は,日本語で表示させることができます。
詳しい挙動は,上にあげたQiitaの記事を見てみてください。
function man() { # Stock current LANG _LANG=${LANG} # set language (but if MANLANG is already set, use that) export LANG=${MANLANG:-${_LANG}} function restore_lang() { # restore LANG and clean up name space export LANG=${_LANG} unset _LANG unset -f $0 } trap restore_lang 1 2 3 EXIT if [ ! -n "$1" ]; then echo "What manual page do you want?" return 1 fi # get man type (using fzf but you can replace it with peco or percol or zaw) word="$(whence -wa -- $1 | uniq | fzf -1 | sed 's/: / /' | cut -d' ' -f2)" # if escaped, do nothing if [ ! -n "${word}" ]; then return 0 fi # switch operation by word case ${word} in builtin) # built-in command local man_indent _space # TODO: get how many spaces before the commands man_indent=7 _space="$(printf '%*s' "$man_indent" '')" /usr/bin/man --pager="less -p'^${_space}\\$1 '" zshbuiltins ;; reserved) # reserved words /usr/bin/man --pager="less -p'^COMPLEX COMMANDS$'" zshall ;; alias) # alias whence -c $1 ;; function) # function if hash pygmentize 2>/dev/null; then whence -f "$1" \ | pygmentize -l sh elif hash highlight 2>/dev/null; then whence -f "$1" \ | highlight --out-format=ansi --src-lang=Bash else whence -f "$1" | ${MANPAGER:-${PAGER:-less}} fi ;; *) # try using man /usr/bin/man "$@" ;; esac }
peco-select-history
よくpecoの使い方の一例として紹介されているアレです。
といっても,自分の場合$PERCOL
(=fzf)を使用しているので,名前は変えるべきなんですが,そのままになっている感じです。
とっても便利で使用頻度も高いです。
function peco-select-history() { typeset tac if hash tac 2> /dev/null; then tac=tac else tac='tail -r' fi BUFFER=$(fc -l -n 1 | eval $tac | $PERCOL) CURSOR=$#BUFFER zle redisplay } zle -N peco-select-history
prompt
全部ここに貼ると長くなるので,リンクを張るだけにします。
Oh-my-zshの中に用意されていたagnoster Themeを元に,自分でいくつか変更したものとなっています。 シンプルな見た目で見やすく,また,コピペするとそのままテストになっているように工夫しました。
例えば,こんな風にコマンドを実行したとして:
tmux上なので,以前紹介したキーバインド^g^c
で内容をコピー+vimで開くと,
のように,カラーバーで見えなかった部分にコメント記号#
が埋め込んでいたため,コピペで表示してもそのまま使える形式になっています。
見た目のことだけを考えるなら,いわゆるPowerlineみたいにしてもいいですし,最近までは▒
や,▓▒░
をセパレータに設定していました。
ここらへんは本当にお好みで,ってかんじですね。
僕は基本的にシンプルなのが好きってのと,下手に色があるよりは,重要な情報だけハイライトして欲しいと思うタイプなので,できるだけ色は抑えめな設定になっています。
ranger
僕が日常的によく使っているソフトウェアにrangerという端末内で動作するファイルマネージャがありますが,これをさらに便利に使うためにzshからも設定したものが以下になります。
機能としては,
q
で抜けた時,シェルでもそのディレクトリに移動- サブシェルにいない時だけ新しくrangerを起動し,そうでないときは
exit
することでrangerの画面に戻る
function ranger() { if [ -z "$RANGER_LEVEL" ]; then local tempfile="$(mktemp -t tmp.XXXXXXX)" # for manual install /usr/local/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}" # for package install # /usr/bin/ranger --choosedir="$tempfile" "${@:-$(pwd)}" test -f "$tempfile" && if [ "$(cat -- "$tempfile")" != "$(echo -n `pwd`)" ]; then builtin cd -- "$(cat "$tempfile")" fi rm -f -- "$tempfile" else exit fi }
termsupport
端末のウィンドウ名などを変更する機能を追加します。
これも,元々はOh-my-zshにあったものを移植してきたものです。
See: http://www.faqs.org/docs/Linux-mini/Xterm-Title.html#ss3.1
precmd_functions
,preexec_functions
に設定することで,コマンドを実行するたびに,端末の名前が更新されるようになります。(実際はtmuxが間に1枚噛む)
function title { emulate -L zsh setopt prompt_subst [[ "$EMACS" == *term* ]] && return # if $2 is unset use $1 as default # if it is set and empty, leave it as is : ${2=$1} case "$TERM" in cygwin|xterm*|putty*|rxvt*|ansi) print -Pn "\e]2;$2:q\a" # set window name print -Pn "\e]1;$1:q\a" # set tab name ;; screen*) print -Pn "\ek$1:q\e\\" # set screen hardstatus ;; *) if [[ "$TERM_PROGRAM" == "iTerm.app" ]]; then print -Pn "\e]2;$2:q\a" # set window name print -Pn "\e]1;$1:q\a" # set tab name else # Try to use terminfo to set the title # If the feature is available set title if [[ -n "$terminfo[fsl]" ]] && [[ -n "$terminfo[tsl]" ]]; then echoti tsl print -Pn "$1" echoti fsl fi fi ;; esac } ZSH_THEME_TERM_TAB_TITLE_IDLE="%15<..<%~%<<" #15 char left truncated PWD ZSH_THEME_TERM_TITLE_IDLE="%n@%m: %~" # Avoid duplication of directory in terminals with independent dir display if [[ "$TERM_PROGRAM" == Apple_Terminal ]]; then ZSH_THEME_TERM_TITLE_IDLE="%n@%m" fi # Runs before showing the prompt function termsupport_precmd { emulate -L zsh if [[ "$DISABLE_AUTO_TITLE" == true ]]; then return fi title $ZSH_THEME_TERM_TAB_TITLE_IDLE $ZSH_THEME_TERM_TITLE_IDLE } # Runs before executing the command function termsupport_preexec { emulate -L zsh setopt extended_glob if [[ "$DISABLE_AUTO_TITLE" == true ]]; then return fi # cmd name only, or if this is sudo or ssh, the next cmd local CMD=${1[(wr)^(*=*|sudo|ssh|mosh|rake|-*)]:gs/%/%%} local LINE="${2:gs/%/%%}" title '$CMD' '%100>...>$LINE%<<' } precmd_functions+=(termsupport_precmd) preexec_functions+=(termsupport_preexec)
まとめ
以上で~/.zsh/functions
で設定している機能の紹介を終わります。
注意深く見てくださった方は気づいていただけたかもしれませんが,変数名や関数名をローカルにすることに気をつけていて,名前空間を汚すことがないようにしています。
今後も,便利そうな設定があれば随時変更していくでしょうし,今後取り組むことが変わったり,端末の使い方が変化したりした場合にはまた大きく変更があるかもしれません。
でもそれはそれでまた育てていく楽しみがあると思うので,むしろ変化があってほしいと思います。(今の環境が快適なだけに)
次回は~/bin
以下に配置しているシェルスクリプトで,使用頻度の高そうなものについて紹介しようと思います。
ここまで見てくださった方,ありがとうございました。