これは、この記事をターミナルで表示したものだ。どうやっているのかは、後で種明かしをする。
\ast \qquad \ast \qquad \ast
最近、古い知人から、昔作ったPerl版のa2psを最新化してほしいという要望を受けた。なにしろ、作り始めたのが1990年、最後に更新したのが2002年というプログラムなので、今の処理系の仕様に対応していないとか、Unicodeが扱えないとか、おそらくそんなことだろう。
a2psは、テキストをPostScriptに変換して、A4用紙に2ページ分を並べて印刷するためのコマンドだ。思えば、昔から横向きの用紙にページを並べて印刷することになぜかこだわりがあった。最初はラインプリンタだ。132カラム、66行のプリンタに、Unixのprコマンドでソースコードを印刷した。多分DECのLP27ではないかと思う。VAX-11/780に、UNIBUSで接続していた。
やがてCanonのLBP-8が会社に導入されると、専用のエスケープシークエンスを使って、文字を拡大したり加工したりするコマンドをcshで作った。そのうち、Iconというプログラミング言語を使って並べて印刷するコマンドを作った。両面印刷するために、片面印刷すると一旦プリンタを止め、裏返して手差しで入れるような機能もあった。1985年から1987年の頃だ。
1990年から、1年ほど設立当初の慶應のSFCキャンパスに居候していたことがあり、最初にa2psを作ったのは、おそらくその時だ。プリンタはImagenだったか、リコーだったか。a2psは、元はCで書かれていたが、日本語処理を入れるのが面倒だったのでPerlで書き直した。ungetcを多用した難解なロジックは、50行程度のコードになった。日本語がまともに使えるということで、それなりに利用者がいた。本家と間違えないようにa2ps.plという名前でfjに流していた。
C版のa2psは、GNUで開発が続いているので、そちらを使えばいいんじゃないのかという気もするが、Unicodeには対応していない。そもそも作者である自分が、もうそんなものは使っていない。しかし、いまだにマルチカラム表示にはこだわりがあって、端末でファイルを読むためにはnupというコマンドを使っている。だから、nupを使えばいいんじゃないの、というのがタイトルの意味だ。
今時のターミナルは、ノートPCでも全画面にすれば、2ページを並べて表示するくらいの余裕は十分にある。ところが、ページャにしても、プレビューアにしても、マルチカラム表示に対応しているものを知らない。Emacsには同一バッファの複数画面を連動させるfollow-modeというのがあるので、エディタやIDEにはそんな機能があるかもしれないが、他にもあったら教えて欲しい。見つからなかったので自分で作った、というのがnupだ。
nupとは
名前は印刷用語のn-upから取った。1枚の紙に2ページ分を印刷するのが2-up、4ページ分なら4-upという具合。ターミナルの画面を紙に見立てて、そこに複数のページを並べて表示する。
インストール
Homebrewを使っていれば、次のようにしてインストールできる。
brew tap tecolicom/tap
brew install app-nup
CPANからインストールする場合はこうなる。
cpanm -n App::nup
nup本体はbashスクリプトで、bash 4.3以降が必要だ。macOSに付いてくるbashは3.2なので(macOS 15.8で確認)、Homebrewなどで新しいbashを入れて、PATHで先に見つかるようにしておく必要がある。
何でも段組みで見る
コマンドの出力を見る
いちばんよく使うのは、コマンドの出力を段組みで見る使い方だ。nupの後にコマンドを書けばいい。
nup man nup
nup perldoc App::nup
出力はページ単位に区切られて、画面の幅に入るだけ横に並ぶ。各ページの下の罫線にはページ番号が入る。全体はlessに渡されるので、スペースキーで次の画面に進める。
標準エラー出力も一緒に段組みされるので、エラーメッセージもページの中に表示される。その代わり、実行したコマンドの終了ステータスは返らない。
manのページャとして使う
manを読むたびにnupと打つのが面倒だったら、MANPAGERに設定しておけばいい。
export MANPAGER="nup -A"
こうしておくと、manを実行するだけで、マニュアルが段組みで表示される。-Aは、ターミナルが狭くて1カラムしか入らない場合にページ区切りをやめるオプションで、詳しくは後で説明する。
ファイルを見る
引数にファイル名を書けば、そのファイルを表示する。
nup README.md
最初の引数が存在するファイルならファイル表示、実行できるコマンドならコマンド実行、と自動的に判断している。実行可能なファイルをコマンドとして実行したい場合は-eオプションを付ける。
nup -e ./script.sh
ファイルを並べて見る
ファイルを複数指定すると、それぞれのファイルを1つのカラムに入れて、横に並べて表示する。
nup file1.txt file2.txt
これはnupが内部で使っているansicolumnの並列表示の機能で、各カラムの上にはファイル名が入る。不要なら--no-filenameを指定する。並列表示では、全体の長さを一番長いファイルに合わせて表示するので、ページ単位の区切りはない。
-V (--parallel)を付けると、ファイルが1つでもこの並列表示のモードになる。この場合は、ページで区切らずに内容全体を1カラムで表示する。
好みに合わせる
カラム数と行数
何も指定しなければ、ターミナルの幅を85で割った数だけカラムを並べる。1カラムの幅は-S (--pane-width)で変えられる。カラム数を直接指定するなら-C (--pane)を使う。
nup -C2 man perl
-Cにカンマで区切って行数も書くと、画面を縦にも分割する。-C2,2なら2カラム2行の4-upになる。
nup -C2,2 man perl
ページの高さを直接指定したい場合は-P (--page)を使う。
ページ区切りをやめる
ページ単位で区切るのをやめて、内容全体を均等にカラムに分けたい場合は-F (--no-paginate)を指定する。
nup -F man perl
ターミナルが狭くて1カラムしか入らない場合は、ページで区切っても読みにくくなるだけだ。-A (--auto-paginate)を指定すると、その場合だけページ区切りをやめる。MANPAGERに設定するときに指定しておくと便利だ。
ページャを選ぶ
nupはデフォルトではless -F +Ggをページャとして使う。-Fは、出力が1画面に収まる場合にはlessをすぐに終了させるオプションだ。+Ggは、いったん最後まで読み込んでから先頭に戻るという意味で、こうしないと-Fやパーセント表示がうまく働かない。最後のページを送ったときに空のページが表示されるのを防ぐ効果もある。これについては以前に記事を書いた。
lessのバージョン590以降には、同じことをする--file-sizeオプションがあるのだが、macOSに付いてくるlessは581なので使えない(macOS 15.8で確認)。新しいlessを使っているなら、次のように設定しておけばいい。
export NUP_PAGER="less --file-size"
ページャは環境変数NUP_PAGERで指定する。PAGERを使わないのは、PAGERにnupを設定した場合に無限ループするのを避けるためだ。ページ単位で表示している場合だけ別のページャを使いたいときはNUP_PAGE_PAGERを設定する。複数ファイルの並列表示にはページ構造がないので、その場合はNUP_PAGERが使われる。なぜこんな環境変数があるのかは、次で説明する。
コマンドラインでは--pagerで指定でき、--no-pagerを指定するとページャを使わずにそのまま出力する。
また、LESSとLESSANSIENDCHARSが設定されていなければ、それぞれ-RとmKを設定してからlessを起動する。色付きの出力や、罫線の後に出てくる行末消去のシークエンスを正しく表示するためだ。
検索してもページがずれないless
nupの出力は、画面1枚がちょうど1ページになるように作ってある。スペースキーで送っている限りはきれいに見えるのだが、lessで検索をすると、見つかった行が画面の一番上に来るように表示されるので、ページの途中から表示されることになる。上の罫線は画面の外に出てしまうし、各カラムが中途半端な位置で切れてしまう。
これを何とかするために、lessを改造して--align-recordというオプションを追加した。検索で移動したときに、見つかった行そのものではなく、その行を含むレコードを画面の先頭から表示するというものだ。レコードの長さは行数で指定する。0なら画面の高さで、負の数ならその分だけ短くなる。lessは画面の最下行をプロンプトに使うので、nupの場合は-1を指定する。
export NUP_PAGE_PAGER="less --align-record=-1"
NUP_PAGE_PAGERは、このようにページ単位の表示のときだけ使いたいオプションを指定するために用意した。並列表示のようにページ構造のない出力でこれを使うと、かえって見にくくなる。
--align-recordには/で始まるパターンを指定することもできて、その場合はパターンに一致する行ごとにレコードを区切る。たとえば--align-record='/^## 'とすれば、Markdownの文書を検索したときに、見つかった行を含む節が先頭から表示される。
改造したlessは、Homebrewでインストールできる。本家のlessと同じ名前のコマンドをインストールするので、すでにHomebrewのlessを入れている場合は、先にアンインストールしておく必要がある。
brew install tecolicom/tap/less
ソースは次のリポジトリにある。
この機能は本家にも提案しているが、取り込まれるかどうかはわからない。メンテナからは、画面の大きさで決まるページに合わせるという用途は特殊すぎるのではないか、という意見をもらっている。言いたいことは理解できるが、こういう使い方もあるということを、なんとかわかってもらいたいと思っている。
罫線と色を変える
罫線のスタイルは--bs (--border-style)で変えられる。デフォルトはheavy-boxだ。指定できるスタイルはansicolumnのマニュアルを見て欲しい。
罫線の色は--cm (--colormap)で変えられる。
nup --cm=BORDER=R man nup
黒板や白板のような配色にするオプションもある。
nup --green-board README.md
--white-board、--black-board、--green-board、--slate-boardの4種類がある。ページ番号が不要なら--no-page-numberを指定する。
コマンドの出力に色を付ける
色を付けて出力するコマンドの多くは、出力先が端末でないと色を付けない。nupを通すとパイプに出力することになるので、そのままだと色が消えてしまう。そこで、いくつかのコマンドについては、色を付けるオプションを自動的に追加するようにしてある。
bat bat --style=plain --color=always
batcat batcat --style=plain --color=always
mdcat mdcat --ansi
rg rg --color=always
tree tree -C
これ以外のコマンドについては、--aliasオプションで指定できる。
nup --alias='grep=ggrep --color=always' grep pattern file
コマンド名を別のコマンドに置き換えることもできるので、上の例ではgrepの代わりにggrepを実行している。
ansicolumnのオプションを直接使う
nupは、内部でoptexコマンドを経由してansicolumnを呼び出している。nupが用意していないansicolumnのオプションを使いたい場合は、ファイル名の後に書けばいい。ファイル表示では、最初のファイル名から後ろの引数はすべてそのままansicolumnに渡される。
nup -C2 file1.txt file2.txt -w 170
これは2カラムで、全体の幅を170にして表示する。
端末がない環境で実行した場合には、環境変数COLUMNSとLINESで画面の大きさを指定できる。
COLUMNS=170 nup --no-pager -C2 file1.txt file2.txt
長い行を折り返す際の禁則処理の幅は--runin、--runout、--runlenで、どこで折り返してよいかは--boundaryで指定できる。これらは~/.ansicolumnrcの設定よりも優先される。
文書を見る
PDFやOffice文書
PDFやWord、Excel、PowerPointのファイルを指定すると、textconvモジュールを使ってテキストに変換してから表示する。
nup report.pdf
nup document.docx
変換には外部のコマンドを使うので、PDFならpdftotextなどがインストールされている必要がある。JPEGのファイルはexifコマンドでExif情報に変換される。対象となる拡張子のデフォルトはpdf,docx,docm,pptx,pptm,xlsx,xlsm,jpg,jpegで、--textconv=で変更でき、--textconv=noneで無効にできる。
Markdownを見るmdee
nupを使っているコマンドを一つ紹介しておく。mdeeは、Markdownのファイルを色付けして表示するビューアで、段組みと表示にはnupを使っている。LLMが出力したMarkdownの文書を読むのに便利だ。こちらについては、また別の機会に紹介したい。
mdee README.md
種明かしをすると、冒頭の画像は、この記事の原稿をmdeeで表示したものだ。
mdee -C3 nup.md
lbppr、a2ps、そしてnup
冒頭で触れた昔のプログラムが、実際にどんなものだったのかを見ておこう。
lbppr
LBP-8用にIconで書いたプログラムはlbpprという名前だった。ソースが残っていて、冒頭はこうなっている。
#
# lbppr.icn: double column print program for CANON LBP-8
#
# Usage:
# lbppr [ options ] [ file ... ] | lpr -Plbp
#
# Options:
# -h header: specify header
# -sort: print by reverse order
# -#: column output by specified number
# -2side: double sided output (of cource manualy)
# -nofold: print without folding (convenient for Japanese output)
#
# Header Format: ("%f [ Page %p ]" by default)
# %f: file name
# %p: page count
# %d: date
# %t: time
# %D: default format
#
# Notes:
# No Japanese 2 byte code output.
#
# June 17 1987
# K. Utashiro
# Software Research Associates, Inc.
#
各ページを枠で囲み、上下にファイル名とページ番号を入れていた。ページ数はオプションで変えられたし、長い行は折り返していた。-2sideを指定すると、まず片面分を出力し、プリンタに用紙の入れ替えを指示するシークエンスを送ってから、残りの面を逆の順序で出力する。当時の一番の目的は、紙の節約だったと思う。
a2ps
Perl版のa2psは、Miguel Santanaのa2psの文字列処理を書き直したもので、PostScriptの部分は元のものに手を入れて使っている。用紙を横にして2ページを並べる2-upで出力するのが基本だった。後に寄せてもらったパッチで1〜5段を選べるようになり、そのときの変数の名前が$nupだった。
今のPerlではそのままでは動かなくなっていたので、少し手を入れて、最初の画像と同じman nupを出力してみた。
GNU a2psの方は、2007年の4.14の後はしばらく間が空いたが、2023年に4.15が出て、2025年12月には4.15.8がリリースされている。Homebrewでもインストールできる。こちらも--columnsや--rowsでn-upの印刷ができる。ただ、UTF-8には対応していないので、日本語を印刷するならEUC-JPに変換する必要がある。
同じものをGNU a2psで出力するとこうなる。ロケールがja_JP.UTF-8なので、左下に入る日付が文字化けしている。
それでも紙に出したいとき
それでも紙やPDFにしたいという場合のために、nupの出力をそのままPDFにする機能も追加した。-oオプションで.pdfで終わるファイル名を指定すればいい。用紙をターミナルの画面に見立てて、画面に表示するのと同じように段組みしたものを出力する。PDFを作るのはansi2pdfという別のコマンドで、こちらはまだ開発中なので、仕様は変わるかもしれない。同じman nupを出力するとこうなる。
nup -o nup.pdf man nup
紙に出さなくても、大きなディスプレイなら、同じマニュアルの全体が1画面に収まる。
ラインプリンタの頃から40年以上たって、紙に印刷していたものが、横に広いターミナルの画面に変わった。目的が紙の節約から広い画面を有効に使うことに変わっただけで、やっていることはあまり変わっていない。
関連記事
nupの表示はansicolumnコマンドに頼っている。ansicolumnについては、ターミナルで複数のファイルを並べて表示する方法を考えた一連の記事で紹介している。nupで複数のファイルを並べて表示できるのも、その成果だ。









