Execute a command in tmux new window

私は普段ターミナルなどのタブ機能ではなく、tmuxのウィンドウを使っている。
メインのウィンドウは常にbashの状態にしておき、vimやtig、manなどは別ウィンドウを開くというようにしている。 これはtmux neww -n vim vimのようにすればできる。
ただ毎回tmux newwを実行するのは少し煩わしい。 そこでちょっとしたbash関数を定義して別ウィンドウで実行したいコマンドをラップするようにしている。
以下にその関数を定義したbashスクリプトを示す。

# These codes are licensed under CC0.
# https://creativecommons.org/publicdomain/zero/1.0/deed.ja
escape_args() {
args=()
for a in "$@"; do
args+=($(printf %q "$a"))
done
echo "${args[@]}"
}
wrap_with_neww_in_tmux() {
local cmd="${1}"
if [[ -z "$(which ${cmd})" ]]; then
return
fi
local window_title="${2:-$cmd}"
echo "$(cat <<EOD
${cmd}() {
if [[ -n "\${TMUX}" ]]; then
tmux neww -n "${window_title}" "command ${cmd} \$(escape_args "\$@")"
else
command ${cmd} "\$@"
fi
}
EOD
)"
}
vim() {
# ^/tmp/bash-fc- is bash vi mode on Ubuntu.
# /private/*/bash- is bash vi mode on Mac.
if [[ -z "${TMUX}" ]] || [[ "$1" =~ ^/.+/bash-fc ]]; then
command vim "$@"
else
if [[ -n "$VIRTUAL_ENV" ]]; then
# for some vim plugin, preserve `VIRTUAL_ENV`
tmux neww -n vim "VIRTUAL_ENV=$VIRTUAL_ENV command vim $(escape_args "$@")"
else
tmux neww -n vim "command vim $(escape_args "$@")"
fi
fi
}
eval "$(wrap_with_neww_in_tmux man man-\$1)"
eval "$(wrap_with_neww_in_tmux tig)"
eval "$(wrap_with_neww_in_tmux ssh)"
eval "$(wrap_with_neww_in_tmux mycli)"
eval "$(wrap_with_neww_in_tmux ipython)"
eval "$(wrap_with_neww_in_tmux ipython3)"
eval "$(wrap_with_neww_in_tmux less)"
eval "$(wrap_with_neww_in_tmux http-prompt)"
view raw commands.bash hosted with ❤ by GitHub

このスクリプト(commands.bash)をHomeディレクトリなどに置き、 .bashrc でsource ~/commands.bashしておけばよい。

ラップした関数はtmux上で実行されたかどうかを判定し、 tmux上であれば別ウィンドウで実行、 そうでなければ通常通り実行するようになっている。

vimだけ専用の関数を定義しているが、これはbashをviモード実行するため。 bashをviモードで利用していて、実行するコマンドを編集するためにvimを起動することもある。 その際にtmuxの別ウィンドウでvimが起動してしまわないようにするための分岐が必要なので、専用の関数を定義している。 bashをviモードで使わない人は専用の関数を定義する必要はない。

新しいコマンドを同様に別ウィンドウで実行したければ、 スクリプト下部のようにeval "$(wrap_with_neww_in_tmux some-command)"とすればよい。