2014年8月7日木曜日

tmux 環境作成自動化ツール tmux-agent 作ってみました

さっそくですが、タイトルのツール tmux-agent: tmux initial action agent を Github で公開してみました。一言で言いますと、所定の書法で記載したファイルを読み込むことで、ウィンドウやペインの作成・コマンド実行・レイアウト変更・同期モードオンなどのアクションを自動実行して tmux を開始してくれるツールです。bash で書いてます。一応 Mac OS X と CentOS 上で動作確認済み。

ちなみに、ほぼツールが出来上がった後に tmuxinator: Manage complex tmux sessions easily なる素晴らしいツールがあることに気づき ( ∑(゚Д゚) ← その時の心境)、 自作のツールをなかったコトにしようかと思いましたが、落ち着いてから見比べるといくつか tmux-agent の方が便利な部分もあったのでやっぱり公開しました。詳しくは tmuxinator との違い にまとめてみましたので、既に tmuxinator をご存じの方は参考までに。

以下、このツールを作った背景とか実際の利用例とかになります。

※ 追記 (2018/05/29) ※ tmux-agent に機能追加しました。詳しくは tmux-agent にプロンプト待ちと進捗メッセージ出力の機能追加 を御覧ください

目次

はじめに

改めましてですが、tmux 便利ですよね。昔は screen 使ってましたが、複数ペインの同時操作機能とかに惹かれて乗り換えて後、数年ほど愛用してます。

ただ、使い慣れてくるほどちょっとした不便さが気になってきまして、例えば

  • 複数ペインでそれぞれ別サーバに ssh ログインして同期モードにする
  • 作業に応じてウィンドウごとに別ディレクトリに移動する
  • (だいたいやることが決まってるような) コマンドの実行
  • ペインのレイアウト変更
  • セッション、ウィンドウのネーミング

とかってことを都度やるのが面倒になってきちゃいまして…。

要するに tmux 立ち上げた後の

  • ウィンドウやペインの作成とネーミング
  • コマンド実行
  • レイアウト変更
  • 同期モードオン

あたり自動化できないものかなー、と思いまして。

そんな時に tmuxで複数サーバの同時オペレーション | NaviPlus Engineers' Blog というブログを目にしまして、これは良いなと。tmux はコマンドベースで上記の操作が行えるので、スクリプト書けば自動化できちゃうわけですよね。

でわでわと用途に応じてスクリプト作ろうかなと思いましたが、スクリプト毎度書くのもやっぱり面倒なと。私の場合、あちこちのサーバにログインすることが多く、ログインの対象に応じて、ペインを複数作って同期モードオンにしたい場合や、個別にウィンドウを立てたい場合があったり、レイアウトを tiled にしたい場合がたまにあったりと、やりたいことの組み合わせもいろいろとありまして、それらのパターンに応じて毎度スクリプト作るのも辛いなぁと思いまして。せめてスクリプトじゃなくてシンプルな書法のファイルを読み込んで、動作を制御できるものがほしいと思ってました。

それで作ってみましたよというのが tmux-agent: tmux initial action agent になります。

インストール

単なる bash スクリプトなので、基本的にはダウンロードして適当なディレクトリに置けば OK なのですが、詳しくは Installation: tmux-agent を参照下さい。

使い方

例えば以下のような ~/.tmux-agent/web-log-sync-ssh-panes を用意しておき、

session: web-log-sync-ssh-panes
  window:
    pane-command: ssh ${pane}
    pane-command: tail -f /var/log/httpd/access_log
    pane-sync:
    pane: ${argv}

こんな感じで実行しますと

$ tmux-agent web-log-sync-ssh-panes web{1,3,5}
  • "web-log-sync-ssh-panes" という名称でセッション開始
  • web1, web3, web5 というホストに対して ssh ログインを実施
  • ログイン実施後に tail -f /var/log/httpd/access_log を実行 (ただし、ssh-agent などによりパスワード入力不要でログインできる状況である必要あり)
  • synchronize-panes モードを on にし、複数ペインに同時入力可能な状態に変更

といったことまでを自動でやってくれます。

イメージは以下のような感じ。

ちなみに、上のファイルを見て「この順番何?」と首をかしげた方もいるかと思いますが、tmux-agent で読み込むファイル内では、実行コマンドを記載する pane-command や、同期モードをオンにする pane-sync などは、対象となるペイン (pane で指定) よりも上に記載 するルールになってます。自分でもファイルのパッと見は奇妙だなぁと思うものの、実装で楽するためにこうしてます (この書式だと、各行の読み込み時に逐次で処理が進められる)。ウィンドウを指定する window なども同様です。書法の詳細については File Format - Keys: tmux-agent に記載がありますので詳しくはそちらで。

ペインだけでなく、複数ウィンドウのスタートも可能です。一つのコマンドを複数のウィンドウで実行することも可能ですし、ウィンドウごとに実行コマンドを変えることも可能です。ここらへんはペインに対しても同様なことができます。

session: host-status${id}
  window-command: df
  window: disk
  window-command: free
  window: memory
  window-command: top
  window: cpu
  window-command: tail -f /var/log/${window}
  window: messages secure maillog

筋は良くないのかもしれませんが、ファイルを読む以外にも、既存セッションが存在する場合はそのアタッチをしたりもします。要するに tmux で作業始める際は全部この tmux-agent から始められるようにしたかったという。

$ tmux-agent <ファイル or 既存セッション名>

同名のファイルとセッションが存在してた場合はファイル優先になります。セッションの読み込みを優先したい場合もあるかと思いますので、そこらへんの使い分け方については後述の 使用例: セッション名の自動採番 を参照下さい。

Bash/Zsh の補完関数利用

Bash, Zsh を利用してる方向けに、補完関数も作ってみました。

それぞれ、source コマンドなどで読み込んでもらうことで、tmux-agent コマンドの後でタブを入力することでファイル名が補完されます。Zsh の場合は既存セッションについても補完されます。

[bash]

$ tmux-agent <TAB>

app-ssh-windows     no-title          tail-webservs-log
multi-ssh-windows   sync-ssh-panes    web-log-sync-ssh-panes

[zsh]

$ tmux-agent <TAB>

multi-ssh-windows   -- init-action
multi-ssh-windows0  -- attached
multi-ssh-windows1  -- detached
no-title            -- init-action
no-title0           -- detached
sync-ssh-panes      -- init-action
tail-webservs-log   -- init-action
tail-webservs-log   -- attached

この補完機能でだいぶ使い勝手は良くなるかなと思ってます。

使用例いろいろ

以下では、自分なりにユースケースに応じて作った機能について、紹介します。

セッション名の自動採番

とりあえず何かしらの作業始める時にと以下のようなファイルを用意しておくとします。

# file: no-title
session: no-title

1度目は以下のようにファイルを読み込み、セッション no-title を起動します。

$ tmux-agent no-title

そんな折、別の作業が入ったとします。セッションをデタッチして、再度同じコマンドでセッションを起動しようとすると、tmux-agent の機能上、既存のセッションをアタッチしてしまいます。

こういったケースで、常に新しいセッションを始めるようにしたい場合には ${id} 変数を使ってみて下さい。

# file: no-title
session: no-title${id}

こうすると、

$ tmux-agent no-title

を実行するごとに、no-title0, no-title1, no-title2 といった、${id} に新しい番号が採番された新しいセッションが作成されます。つまり、新しい作業を始める場合は常に上記のコマンドでよく、既存のセッションをアタッチしたい場合は

$ tmux-agent no-title2

などとすれば良いわけですね。

これは用途ごとに要否が異なる機能だと思いますので、使い分けていただければ幸いです。

ちなみに、以下のように session の中身が空の場合はデフォルト値として ${file}${id} が入りますので、その手の用途の場合は空にしておく方が楽かもしれません。(${file} はファイル名に自動変換されます)

# file: no-title
session:

レイアウト設定

以下のような指定で、ペインのレイアウト指定も行えます。

# file: topdirs-sync-panes-tiled
session: 
  window: 
    pane-command: cd ${pane}
    pane-command: ls
    pane-layout: tiled
    pane: /usr /var /tmp /bin

2014/08/07 時点での 最新版である tmux 1.9a では以下のレイアウトが指定可能です。

  • even-horizontal
  • even-vertical
  • main-horizontal
  • main-vertical
  • tiled

個人的には作業に応じて、even-vertical, even-horizontal, tiled を使い分けてますね。

カスタムレイアウト設定

tmuxのwindow, pane設定を一発で再現できるtmuxinatorが便利 で紹介されていて「こんな方法があるのか!」と初めて知ったのですが、ビルトインのレイアウトの他にも、自作したレイアウトを再現することも可能です。

もともと tmux にある機能で、まず以下のコマンドで既存セッションのレイアウト一覧が取得可能です。

$ tmux list-window -a -F "#S: #W: #{window_layout}"

session: window: 77cf,80x24,0,0{54x24,0,0[54x16,0,0,828,54x7,0,17,830],25x24,55,0,829}

この 77cf,80x24,0,0{54x24,0,0[54x16,0,0,828,54x7,0,17,830],25x24,55,0,829} の部分がレイアウトになっており、select-layout にこの文字列を与えることでレイアウトが再現されます (レイアウト取得時とペイン数が同じ事が前提です)。

tmux-agent でもファイルの pane-layout: にレイアウトをセットしておくことで、カスタムレイアウトを再現可能です。

# file: custom-layout
session:
  pane-layout: 77cf,80x24,0,0{54x24,0,0[54x16,0,0,828,54x7,0,17,830],25x24,55,0,829}
  pane: {0..2}

ちなみに、もう少し複雑なレイアウトのサンプルを tmux-agent: init-action-files.sample/custom-layout-sample に用意してます。

tmuxinator との違い

tmuxinator は非常に良さ気なので基本そちらを使ってもらえればイイと思うのですが、いくつか tmux-agent 特有の機能もありますのでまとめました。(2014/08/07 時点)

tmux-agent 特有の機能

  • Bash のブレース展開が利用可能 (window: や pane: の指定において)
      • server-{a,b,c} → server-a server-b server-c
      • log.080{4..8} → log.0804 log.0805 log.0806 log.0807 log.0808
  • 同じようなコマンドを複数のウィンドウやペインで実行する場合、以下のようにシンプルに記載可能。以下の例では、対象サーバ 6 つに対してそれぞれウィンドウを開き、その中でそれぞれログごとに 3 つのペインを開いてコマンドを実行する。

    # file: remote-servers-log-display
    session:
      window: app{8,9} web{3..6}
        pane-command: ssh ${window}
        pane-command: tail -f /var/log/${pane}
        pane: messages secure cron
    
  • tmux-agent のコマンドライン引数に与えた文字列を ${argv} 変数を介してコマンドに組み込んで実行可能。上記のいくつかの例で示すように、ログイン先ホスト名などをコマンドライン引数で動的に変更できる。
  • ${id} 変数の利用により、一つのファイルから複数のセッションを開始することが可能。

tmux-agent 微妙だなぁと思う部分

  • tmux-agent のファイルは File Format: tmux-agent に記載する通りのオレオレな適当なものであるのに対し、tmuxinator はまっとうな Yaml のフォーマットであるため、ウィンドウ・ペイン・コマンド等の関係が綺麗に表現されています。そのため後者の方が読み書きも容易かと。tmux-agent のファイルは bash でお手軽に実装するのを優先したため、このような少々妙なフォーマットになってます。
  • pre, pre_window (全体の前処理、各ウィンドウを開く際の前処理) などの気の利いた設定が tmux-agent にはありません。(他にも socket_name, tmux_options, tmux_command など)
  • tmuxinator は tmuxinator open [project] で直接ファイルをエディタから編集する機能を備えてます。tmux-agent はそういった気の利いたものはありません ^^; (直接 vi なり emacs なりで編集お願いします)

終わりに

その他詳細は README: tmux-agent にて拙い英語にて紹介してます。

Sample Files も用意してますので、自作のファイルを作る場合はそれを元に作っていただくのが良いかと。編集の際には File Format をご覧ください。

tmux をある程度使い込んでる方には、tmuxinator なり拙作 tmux-agent なりは使って損はないツールかなという気がしてます。みなさんのターミナル生活がより快適なものになれば幸いです。

それでわ

参考資料