- ターミナルでWeb記事を直接読みたいユーザーのために、James' Coffee Blog はブログ記事を Linuxマニュアルページ 形式でも提供している
- 同じURLでも、クライアントが
Accept: text/roff を送ると HTML の代わりに roff ドキュメント を受け取れるよう、HTTPコンテンツネゴシエーションを利用している
- 各記事の
.man ファイルは、TITLE、AUTHOR、PUBLISHED、POST、URL の各セクションを持つテンプレートから生成される
- 本文には Markdown原文 を入れて HTML より読みやすくしているが、マニュアルページでは間隔が常にきれいに揃うわけではない
- NGINX が
text/roff リクエストを検知して URL を .man ファイルに書き換えるため、curl で保存してから man ./post.page のように開ける
ブログ記事を man で読む
- Linux の マニュアルページ は、コマンドの使い方をターミナルで確認する基本的な方法で、通常は
man <command> で開ける
- たとえば
tac コマンドのマニュアルは次のように確認する
man tac
- James' Coffee Blog は、Web ブログ記事も同じ方法で読めるように、記事URLから
roff 版をダウンロードして man で開く流れを構成している
- 実際のリクエスト例は次のとおり
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page && man ./post.page
HTTPコンテンツネゴシエーションで形式を選ぶ
- 実装の中心は、クライアントが欲しい応答形式をサーバーに伝える HTTPコンテンツネゴシエーション である
Accept ヘッダーは希望するコンテンツタイプを伝えるために使われる
- たとえば
Accept: image/png は、可能なら PNG ファイルを送ってほしいという意味である
- 複数のコンテンツタイプと優先順位を指定することもできるが、ここでは特定形式の要求だけを使っている
- ブログ記事をマニュアルページ形式で受け取りたいときは
Accept: text/roff ヘッダーを送る
- サーバーはこのヘッダーを見て、HTML の代わりに
man で開ける text/roff 応答 を返す
.man ファイルの生成方法
- Linux のマニュアルページは roff 構文で書かれる
- サイトは、各ブログ記事ごとに
man ページ版を生成するよう修正された
- 使用したテンプレート構造は次のとおり
.TH jamesg.blog 1 "" "jamesg.blog"
.SH TITLE
...
.SH AUTHOR
James' Coffee Blog (https://jamesg.blog)
.SH PUBLISHED
...
.SH POST
...
.SH URL
...
- テンプレートはドメイン名をヘッダーに置き、5つのセクションを作る
- TITLE
- AUTHOR
- PUBLISHED
- POST
- URL
- 本文には Markdown原文 を使用する
- マニュアルページでは間隔が常にうまく揃うわけではない
- それでも HTML より読みやすく、プレーンテキストよりも見出しや段落区切りの情報損失が少なかった
curl で受け取り man で開く
- ブログ記事の
roff 版は次のコマンドでリクエストできる
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page
- 保存した結果はローカルのマニュアルページのように開ける
man ./post.page
- 通常のブラウザが同じ記事URLをリクエストすると HTML 版を受け取る
- 一方、上の
curl コマンドは同じURLに対して text/roff 版 を明示的に要求する
NGINX で .man ファイルへ書き換え
- サーバーは NGINX 設定を数行追加することで
text/roff リクエストを別処理している
/etc/nginx/nginx.conf には、特定のコンテンツタイプが検知されたときにフラグを立てる変数を宣言する
map $uri $redirect_suffix {
~^/(.*)/$ $1;
default "";
}
map $http_accept $redirect_location {
default "";
"~^text/roff" 1;
}
- サイト設定ファイルである
/etc/nginx/sites-enabled 配下には、roff ページのリクエストを処理するルールを追加する
server {
...
location / {
if ($redirect_location = 1) {
rewrite ^/(.*)/$ /$1.man last;
}
...
}
}
- この設定は
Accept: text/roff ヘッダーがあるとき、URL の末尾スラッシュを取り除いて .man を付ける
- その結果、NGINX は各記事の
index.html の代わりに対応する .man ファイル を読む
- 同じブログ記事をWebブラウザでは HTML で、ターミナルでは Linux マニュアルページとして読める構成になる
1件のコメント
Hacker Newsのコメント
ブログ購読の方法として deb リポジトリを提供すると面白そう
apt updateですべての記事を取得し、man your-blogで最新記事と全記事の索引リンクを見られるようにする感じ購読するのは怖そう
これらは dwww パッケージでローカルから閲覧できる: “Read all on-line documentation with a WWW browser”
https://packages.debian.org/bookworm/dwww
Joerg Jaspert はかつての Linux Gazette パッケージメンテナーだった: https://people.debian.org/~joerg/ (2002)
OS に情報配信とドキュメントを統合した例としては、これまで見た中でもかなり優れたものの一つで、とくに従来の端末ベースのインターフェイスよりも man/info 文書を有用にしてくれる
Debian 関連ブログの Debian Planet もあるが、Debian 自体のパッケージとして提供されたことはなかったようだ
正直、ブログ購読には RSS のほうが良い選択である可能性が高い
https://github.com/capjamesg/jamesg.blog.deb に、以下のコマンドで man ページだけを含む deb ファイルを作れる内容がある
git clone [https://github.com/capjamesg/jamesg.blog.deb](<https://github.com/capjamesg/jamesg.blog.deb>)cd jamesg.blog.debdpkg-deb --build --root-owner-group jamesg.blogsudo dpkg -i jamesg.blog.debすると
Processing triggers for man-db (2.9.1-1) ...のような出力が見えるはずで、これはman jamesg.blog用のマニュアルページが利用可能になったという意味今はプレースホルダーしかなく、たぶん明日仕上げると思う
近いうちにブログ記事になるかもしれない
fork したり中間ファイルを使ったりする必要はなく、そのまま
manにパイプできるcurl -sL -H "Accept: text/roff" [https://jamesg.blog/2024/02/28/programming-projects/](<https://jamesg.blog/2024/02/28/programming-projects/>) | man -l -{curl,wget}をコマンドにパイプする議論が始まる友人なら、友人に ストリームをコマンドへ直接パイプさせたりしない
https://news.ycombinator.com/item?id=39554044
参考までに、
curl -sL -H "Accept: text/roff" [https://jamesg.blog/2024/02/28/programming-projects/](<https://jamesg.blog/2024/02/28/programming-projects/>) | man -l /dev/stdinは自分の環境では動くroff ファイルをローカルに保存する必要はない
bashのようなものへ直接パイプするのは、一般に 悪い慣行と見なされる個人的には問題ないと思う。セキュリティ上の意味を理解している人は、こうした変換方法もほぼ確実に知っているので、わざわざ教える必要はない
しかし初心者に教えるにはよくない。いつか痛い目を見るかもしれない。腕が上がれば自然とこういう機能を知るようになるだろうし、その頃には含意も学んでいてほしい
自分が書いた記事ではない: https://www.seancassidy.me/dont-pipe-to-your-shell.html
/usr/bin/man: illegal option -- lMac でパイプを使うワンライナーを作ろうとしたが、ずっとエラーになった
macOS の
man実装には-lフラグがない。マニュアルページで確認したman -l <(curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/)ターミナルで面白いことをするURLの話なら、以前 textfiles.com で見たものがある
VT100ターミナルコードで短いアニメーション映画を見せる形式で、すべて1つのURIから提供される
現代のシステムでは速度制限をかけて見ることができる
curl --limit-rate 1000 [http://textfiles.com/sf/STARTREK/trek.vt](<http://textfiles.com/sf/STARTREK/trek.vt>) && resetresetはターミナルが壊れる可能性があるので入れているほかのターミナルベースのURIとしては、
curl cheat.sh/tarは/の後ろのプログラムの使用例を取得し、curl wttr.in/berlinはターミナル用の書式が適用された天気情報を取得する実際にはかなり単純で、いちばん難しい部分はフレーム生成である
これは
ffmpeg+img2txt.pyでできる: https://github.com/bfontaine/RickASCIIRoll/tree/master/movie...https://16colo.rs/ の古いミラーがあり、これまで公開されたANSIアートの大半を見ることができる
例:
curl ansi.hrtk.in/ungenannt_1453.anshttps://itsfoss.com/star-wars-linux/
trittyを使うと 1200/9600 BPS の転送速度をまねできるあとはMarkdownをroffに変換するコンバータだけが必要だが、探してみたらすでにある
https://github.com/postmodern/kramdown-man
https://rtomayko.github.io/ronn/ronn.1.html
https://kristaps.bsd.lv/lowdown/
[0]: https://pandoc.org/
md2groffは suckless/2f30/cat-v 系のコミュニティにかなり前から存在していたhttps://codeberg.org/nereusx/md2roff
Emacsパッケージの中に、AbelsonとSussmanの SICP をInfoディレクトリにインストールしてくれるものがある
M-x package-install sicp RETと入力するだけでよいこれを見て、改造したフィードリーダーでブログアーカイブの本棚全体をインストールすることもできそうだと思った
EmacsでInfoを読むとブックマークも使える
chicken-schemeもインストールすればよい。その後rootで実行するchicken-install srfi-203chicken-install srtfi216SICP用の
~/.csircは次のとおり(import scheme)(import (srfi 203))(import (srfi 216))(define (inc x) (+ x 1))(define (dec x) (- x 1))その後は通常どおり、ユーザーのgeiserとchicken用のgeiserを使えばよい
インターネットで探せば答えは分かるかもしれないが、HNで聞きたい
高校時代にHP-UXで、誰かが下線付きの単語、つまりセクション参照へ何らかのキー組み合わせを押してジャンプするのを見せてくれた記憶があるのだが、どうしてもどのキーだったのか分からない
man(1)とman(7)も確認したが見つからなかった。偽の記憶かもしれないman ohmanは本質的にnroff -man /usr/share/man/man1/ohman.1 | $PAGERだという点を考える必要があるつまりmanやnroffと対話しているのではなく、ページャ と対話しているのである
今では
lessが最も一般的で、moreも実質的にlessである可能性が高いが、昔はほかにもあり、HP-UXはpgのようなものを使っていた可能性があるpgはAT&T系、moreはBSD系、lessはGNU系だった3つとも
/で正規表現検索を開始するので、下線の有無に関係なく探せるlessはタグファイルにも対応しており、tで次のタグへジャンプできるdthelpviewを思い浮かべているのかもしれない。これがmanページを表示していた可能性があるinfoコマンドで開く texinfo のように聞こえる皮肉なことに、もともとgroff文書のかなりの部分はtexinfoで書かれている: https://lists.gnu.org/archive/html/groff/2005-10/msg00107.ht...
なぜこの些細な点が自分の揚げ足取り本能を刺激したのか分からない。インターネット上の誰かがちょっと間違っていたからかもしれない
最初から不必要に Linux 中心だったからか、あるいは何か別のものを期待していたのに結局 NGINX のコンテンツネゴシエーションの短いデモだったからかもしれない
ともあれ、あえて言いたいどうでもいい点がいくつかある
厳密に言うと、返しているのは roff ではない。
.THのようなものは roff そのものではなく、man ページ作成用のマクロパッケージの一部Markdown-to-roff 変換がなくてがっかりした。そこがこの記事の面白い部分になると思っていたし、少なくとも既存のツールを1つは使えたはず
同様に、そのせいでテキスト整形も実はきちんと合っていない。roff の入力は、文末の
.と別用途の.を区別するため、1文を1行にすることを想定しているまた、
.で始まるすべての行がコマンドとして解釈され、問題を起こす可能性があるそれとも単に自分が意地悪な老人なだけかもしれない
groffやnroffのような別のツールもあって、さらに混乱した「roff/man page/nroff/その他の派生が何で、どう使うのか」だけを説明する記事でも、十分にブログ記事1本になる
短くて明確な説明があれば自分にとっても良かっただろうし、他の人にも役立ちそう
Markdown-to-roff は v2 として考えていた。パーサーを実装しようかと考え始めたとき、誰かが https://github.com/sunaku/md2man を教えてくれて、この問題は解決しそうに見える
GitHub Pages 上で動いている自分の Python サイトにこれをどう統合するか調べる必要があり、少し手を入れなければならない
Pandoc は Markdown を man-page roff にとても簡単に変換できる
それを所定のテンプレートに入れれば、実際の man ページらしくもっとよく見えるはず
正しいメディアタイプは RFC 4263 に基づくと text/troff: https://www.rfc-editor.org/rfc/rfc4263.html
すばらしいアイデア。あとは「自分のブログ記事をプレイ可能な DOOM WAD として提供する」が出てくるまでタイマーを測ればいい