複数のファイルを並べて表示する方法について考える
そう頻繁なわけではないが、2つあるいはそれ以上のファイルを並べて表示したいことがある。そんな時にどうしたらいいかという話。
検索してみるとこんな記事があった。
Linux Shell Tips の回答
詳しくは記事を読んでもらうとして、簡単にまとめると次の通り。
- pr コマンドを使う
- sdiff コマンドを使う
- paste と column コマンドを使う
手元の環境は Linux ではなく macOS だが、それぞれについて検討してみる。
pr
pr コマンドは、本来ファイルの内容を印刷するためのものであり、-m オプションは複数のファイルを並べて表示するためのもので、目的にぴったりなコマンドだ。その昔、ラインプリンタでソースコードをプリントアウトする時に活躍した。-t オプションでヘッダとトレイラーを省略すれば、ほぼ期待通りの動作をしてくれる。
問題は2つある。1点目は、それぞれの行を表示幅で切ってしまい、溢れた内容は表示されない。これは、fold コマンドを使って行を折り返してから表示することで対応は可能だ。
2点目は、少なくとも macOS の pr コマンドは、まるで日本語に対応していないということだ。最近のコマンドは、結構 UTF-8 の多言語文字には対応していて、fold コマンドなんかは上手に処理してくれる。しかし pr はまったく駄目。おそらく、今時 pr コマンドを使う人なんかいないので、進化を止めているのだろう。
sdiff
sdiff は、もともと System V にあったコマンドで、2つのファイルの diff をとって、右と左にわかりやく表示するためのものだ。実は、上の記事では、似通ったファイルを同時表示して比較したいという課題を解決しようとしているので、sdiff はその目的に沿ったツールだ。
sdiff にも pr と同じで、表示幅以上のテキストを削除してしまうという問題がある。やはり fold コマンドで事前に処理することで対応は可能だ。また、3つ以上のファイルには対応できない。
sdiff の最大の問題は、diff をとってしまうということだ。そのためのツールなので仕方がないのだが、全然関係のないファイルを並べて表示しようとしても、やはり共通の部分を見つけて、その部分を並べて表示しようとする。すると、空白行だけが並んで表示されて、ほかはスカスカの部分ができてしまったりする。
ちなみに、diff コマンドに -y, --side-by-side オプションが実装されていれば、sdiff と同じように動作する。macOS の diff でも利用可能だ。
sdif
昔話をすると、sdiff を最初に知ったのは1986年のことだ。シカゴにある Lachman Associates という会社を数日間訪問し、System V 上の NFS の実装に関する技術研修を受けた。その時使ったのが、カーネルソースの sdiff の結果をプリントアウトしたものだった。このツールは素敵だと思ったが、BSD にはなかったので自分でクローンを作って sdif という名前にした。sdif は、今でも開発を続けていて git の diff を表示するのに毎日活躍している。sdif にはまさに上の問題を解決するための --view というオプションがあり、指定すると左右での位置合わせを行わない。行の折り返しに対応し、日本語もまったく問題なく処理するし、余計なデコレーションを外す機能もあるので、実は2つのファイルを並べて表示するだけなら、ほぼ期待通りの結果を得ることができる。
不満点を言えば、もともとそのためのツールではないので、やや苦し紛れ感があるのと、3つ以上のファイルを扱えないことだ。実は、3つまでは対応しているのだが、それを使うのにはもう少し工夫が必要で、そこまでする価値はないので試していない。
追記:その後 sdif を conflict marker 形式に対応させたので、diff3 の -m オプションと組み合わせることで、3つのファイルを並べて表示することができるようになった。さらに -V3 オプションを組み合わせることで、ほぼ望みの結果を得ることができる。(2023-11)
paste と column
paste コマンドで、それぞれのファイルの同じ行をタブでつないで、column -t でそれを整形して表示するというもの。
これもちょっとしたファイルならうまくいく。しかし、1パラグラフ1行みたいなファイルが出てくるとやはり困る。
もう1つの問題は、column が空の項目を無視してしまうことだ。だから、左側のファイルに空行があると、そこに右のファイルの内容が表示されてしまう。左側のファイルが先に終わってしまった時も同様。
これらは、fold で折り返して、sed で空行をなんとかするなどの方法で対応することはできる。ただ、paste も column も、データを処理することを目的としていて、一般的なテキストファイルを対象とはしていないので、どうもしっくりとした解決にはならない。
この例のように、複数のツールを組み合わせて解決する方法は、他にもいろいろ考えられると思う。paste とセットの lam や、あまり知らないかもしれないが rs なんかも使えるのではないかと思う。実際のところ、なんとかしてファイルの長さを揃えて、column コマンドに処理させるのが一番素直な解決法ではないだろうか。
その他のアイデア
もう少し詳しい記事
調べると、同じような内容だがもう少し詳しい記事があった。上で指摘したような問題やその解決法についても書いてあるし、Linux 版の column コマンドには -n オプションがあって空白行を無視しないこともわかった。このあとで触れる tmux についても書いてある。
paste と expand
stack overflow に秀逸な回答があった。paste を使うのは上と同じだが、タブで区切られたフィールドを揃えるのに expand コマンドを使うというのだ。たとえば、1行が50文字より短いことがわかっていれば expand -50
のように、タブ幅を50に指定すればいい。column よりもスマートで、空のフィールドも無視しない。
エディタを使え
複数のファイルを並べて表示したいのなら、エディタを使えという意見はあると思う。その通りだ。
自分は Emacs 使いなので、C-x 3
にバインドされている split-window-right
を使えばウィンドウを縦に分割することができる。その上で scroll-all-mode
を有効にすると、すべてのウィンドウを同時にスクロールすることができる。
VScode でも Atom でも、今時のエディタや IDE にはマルチウィンドウ対応なので、それを使えば簡単に実現できる。Vim でも、使い方はよくわからないけど、一応できた。
ターミナルソフトを使え
これまたその通り。マルチウィンドウ環境であれば、ターミナルソフトを2枚開いて並べて表示すればいいし、iTerm のように、画面を分割できるものもある。あるいは、screen や tmux コマンドを使って、ターミナルの中を分割するという方法もある。
実のところ、なんとかしようと思えば方法はいくつもあることが、複数ファイルを表示するための手軽なコマンドが存在しないことの理由なのではないかと思う。誰でも欲しいと思うことはあるし、実装もそれほど難しくないけど、少し手間をかければ完璧とはいかなくても大方解決できる問題なので、あえてツールを作ろうとは思わなかったのではないだろうか。
ansicolumn を改造する
古くからある UNIX ツールの中では、pr が最も目的に即したものだが、元々ラインプリンタでの出力を想定したもので、進化を止めている。それ以外では、本来データをマルチカラムで表示することを目的とした column コマンドが最も筋がいいのではないかと思う。
column コマンドについては、ANSI エスケープシークエンスによる色付きのテキストも処理できるようにした ansicolumn というコマンドを作ってあった。かねてより、これに手を入れれば複数ファイルの同時表示が簡単にできるのにと思っていたのだが、プログラムの構造上、どうも無理矢理な実装になってしまいそうで躊躇していた。
今回、プログラムの基本的な構造を作り直して実装しやすい状態にしてから、複数ファイルを同時に表示する --parallel オプションを追加した。ansicolumn コマンドとその改造については別の記事に書く。