Linux で出力を別の shell に pts 経由で表示する

Intro

tmux, screen, terminal のタブなど、 shell を複数起動する方法はいくつかある。

Linux では、 pts を経由すれば、ある shell の出力を簡単に別の shell で表示することができる。

これを応用すると、簡易ダッシュボードを作り色々便利に使うことができる。

stdout/stderr

代表例として、 tmux で pane を分割し、コマンドの出力を stdout/stderr で分けて pane ごとに表示するケースで解説する。

まず、以下のようにランダムにエラー出力を吐くプログラムを実行する。

#!/usr/bin/env ruby

while true do
  if rand(0..1) == 0 then
    STDOUT.print "hello\n"
  else
    STDERR.puts "world\n"
  end
  sleep(1)
end

ターミナル上では stdout/stderror の出力は同じ画面上に出る。

$ ./hello_world
hello
world
hello
world
world
world
world
hello
world

tmux で別のペインで表示したい場合に、例えば一旦別のファイルに追記し、ペインごとに tail -f することもできる。

$ ./hello_world 1> ./success.log 2> ./error.log
# pane1 for stdout
$ tail -f access.log
# pane2 for stderr
$ tail -f error.log

この一時ファイルに相当するものとして pts を代用できる。

/dev/pts

tmux で pane を開いた状態で、 tty コマンドで紐付いた pts を確認する。

(ps で確認することもできる)

$ tty
/dev/pts/2
...

紐付いている pts の実態は /dev/pts/2 である。

試しに、別の pane からこの pts に対して書き込みをすると、結果が表示されることがわかるだろう。

## 別の pane
$ echo hello > /dev/pts/2
# pane1 for stdout
$ hello # 表示される

逆に、別の pane から cat で読み出すと、入力された値を奪い取ることもできる。

## 別の pane
$ cat /dev/pts/2
# 入力が表示される
$ # ここでの入力は奪われる

つまり、 pts への書き込みは pane に表示され、 pane への入力は pts から読み出せる。

pts は疑似端末であり、 tmux と shell に間に挟まった中継役のようなものだと思えば良い。

例えば、実行結果の stdout を pane1 に、 stderr を pane2 に分岐して出力したい場合。

もし pane1 が /dev/pts2/ に、 pane2 が /dev/pts5/ に紐づくとすると。

$ ./hello_world 1> /dev/pts2 2> /dev/pts5

これで、結果を 2 つの pane に分けて出力することができる。

応用

tee コマンドと、プロセス置換を使うと、出力を自由にフィルタし分岐することができる。

その分岐先を、複数の pane に分けて指定すれば、簡易ダッシュボードを作ることができる。

例えば access_log をステータスコードごとに出す場合は以下のようになる。

$ tail -f access.log | tee >(grep 404 > /dev/pts/5) >(grep 500 > /dev/pts/6) >(grep 451 > /dev/pts/7)

Logger のタグ (Info, Debug, Trace etc) を使った分岐なんかも地味に便利だったりする。

$ tail -f debug.log | tee >(grep Info > /dev/pts/5) >(grep Debug > /dev/pts/6) >(grep Trace > /dev/pts/7)