4 điểm bởi GN⁺ 2024-03-01 | 1 bình luận | Chia sẻ qua WhatsApp
  • Để phục vụ người dùng muốn đọc bài viết web trực tiếp trong terminal, James' Coffee Blog cũng cung cấp bài viết blog ở định dạng trang hướng dẫn Linux
  • Dù cùng một URL, nếu client gửi Accept: text/roff thì sẽ nhận về tài liệu roff thay vì HTML bằng cách dùng cơ chế thương lượng nội dung HTTP
  • Mỗi tệp .man của bài viết được tạo từ một mẫu có các phần TITLE, AUTHOR, PUBLISHED, POST và URL
  • Phần nội dung chính dùng Markdown gốc để dễ đọc hơn HTML, nhưng khoảng cách trong trang hướng dẫn không phải lúc nào cũng được căn thật gọn gàng
  • NGINX phát hiện yêu cầu text/roff và viết lại URL sang tệp .man, nên có thể lưu bằng curl rồi mở như man ./post.page

Đọc bài viết blog bằng man

  • Trang hướng dẫn của Linux là cách cơ bản để xem cách dùng lệnh trong terminal, thường có thể mở bằng man <command>
  • Ví dụ, có thể xem hướng dẫn của lệnh tac như sau
man tac
  • James' Coffee Blog cũng dựng quy trình để bài viết blog trên web có thể được đọc theo cùng cách đó: tải phiên bản roff từ URL bài viết rồi mở bằng man
  • Ví dụ yêu cầu thực tế như sau
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page && man ./post.page

Chọn định dạng bằng thương lượng nội dung HTTP

  • Trọng tâm của cách triển khai là thương lượng nội dung HTTP, nơi client báo cho server biết định dạng phản hồi mà nó muốn
  • Header Accept được dùng để truyền loại nội dung mong muốn
    • Ví dụ Accept: image/png có nghĩa là nếu có thể thì hãy gửi tệp PNG
    • Cũng có thể chỉ định nhiều loại nội dung và mức ưu tiên, nhưng ở đây chỉ dùng yêu cầu một định dạng cụ thể
  • Khi muốn nhận bài viết blog dưới dạng trang hướng dẫn, hãy gửi header Accept: text/roff
  • Server sẽ nhìn vào header này và trả về phản hồi text/roff có thể mở bằng man thay vì HTML

Cách tạo tệp .man

  • Trang hướng dẫn Linux được viết bằng cú pháp roff
  • Trang web đã được chỉnh sửa để tạo phiên bản trang man cho từng bài viết blog
  • Cấu trúc mẫu được dùng như sau
.TH jamesg.blog 1 "" "jamesg.blog"
.SH TITLE
...
.SH AUTHOR
James' Coffee Blog (https://jamesg.blog)
.SH PUBLISHED
...
.SH POST
...
.SH URL
...
  • Mẫu này dùng tên miền làm header và tạo năm phần
    • TITLE
    • AUTHOR
    • PUBLISHED
    • POST
    • URL
  • Phần nội dung chính dùng Markdown gốc
    • Khoảng cách trong trang hướng dẫn không phải lúc nào cũng khớp đẹp
    • Tuy vậy, nó vẫn dễ đọc hơn HTML và ít làm mất thông tin về tiêu đề, đoạn văn hơn so với văn bản thuần túy

Tải bằng curl và mở bằng man

  • Có thể yêu cầu phiên bản roff của bài viết blog bằng lệnh sau
curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/ > post.page
  • Kết quả đã lưu có thể mở như một trang hướng dẫn cục bộ
man ./post.page
  • Nếu trình duyệt thông thường yêu cầu cùng URL bài viết đó, nó sẽ nhận phiên bản HTML
  • Ngược lại, lệnh curl ở trên yêu cầu rõ ràng phiên bản text/roff cho cùng URL đó

Viết lại sang tệp .man trong NGINX

  • Server xử lý riêng các yêu cầu text/roff chỉ với vài dòng cấu hình NGINX
  • Trong /etc/nginx/nginx.conf, có khai báo các biến đặt cờ khi phát hiện loại nội dung cụ thể
map $uri $redirect_suffix {
~^/(.*)/$ $1;
default "";
}
map $http_accept $redirect_location {
default "";
"~^text/roff" 1;
}
  • Trong tệp cấu hình site dưới /etc/nginx/sites-enabled, thêm quy tắc xử lý yêu cầu trang roff
server {
...
location / {
if ($redirect_location = 1) {
rewrite ^/(.*)/$ /$1.man last;
}
...
}
}
  • Cấu hình này sẽ bỏ dấu gạch chéo cuối của URL và thêm .man khi có header Accept: text/roff
  • Kết quả là NGINX sẽ đọc tệp .man tương ứng thay vì index.html của từng bài viết
  • Nhờ đó, cùng một bài viết blog có thể được đọc dưới dạng HTML trên trình duyệt web và dưới dạng trang hướng dẫn Linux trong terminal

1 bình luận

 
GN⁺ 2024-03-01
Các ý kiến trên Hacker News
  • Sẽ rất hay nếu cung cấp một kho lưu trữ deb theo kiểu đăng ký theo dõi blog
    Kiểu như dùng apt update để tải về toàn bộ bài viết, rồi dùng man your-blog để xem bài mới nhất và liên kết tới chỉ mục toàn bộ bài viết

    • Bản thân ý tưởng thì tuyệt vời, nhưng nếu nó lan rộng, cơ hội phát tán mã độc vốn có trong cách làm này cũng khá rõ ràng
      Có lẽ sẽ thấy sợ khi đăng ký theo dõi
    • Cũng có tiền lệ rồi. Trước đây Debian từng cung cấp cách truy cập Linux Gazette nay đã biến mất, và hiện vẫn cung cấp nhiều gói thông tin như tài liệu gói, trang manual, trang info, RFC, Linux HOWTO, v.v.
      Có thể xem chúng cục bộ bằng gói dwww: “Read all on-line documentation with a WWW browser”
      https://packages.debian.org/bookworm/dwww
      Joerg Jaspert từng là người bảo trì gói Linux Gazette: https://people.debian.org/~joerg/ (2002)
      Đây là một trong những ví dụ hay nhất tôi từng thấy về việc tích hợp truyền tải thông tin và tài liệu vào hệ điều hành, đặc biệt làm cho tài liệu man/info hữu ích hơn so với giao diện truyền thống dựa trên terminal
      Cũng có blog liên quan đến Debian là Debian Planet, nhưng có vẻ nó chưa từng được cung cấp dưới dạng gói của chính Debian
      Thành thật mà nói, RSS có lẽ là lựa chọn tốt hơn cho việc đăng ký theo dõi blog
    • Tôi đang làm phần này
      https://github.com/capjamesg/jamesg.blog.deb có nội dung để tạo một file deb chỉ chứa trang man bằng các lệnh sau
      git clone [https://github.com/capjamesg/jamesg.blog.deb](<https://github.com/capjamesg/jamesg.blog.deb>;)
      cd jamesg.blog.deb
      dpkg-deb --build --root-owner-group jamesg.blog
      sudo dpkg -i jamesg.blog.deb
      Khi đó bạn sẽ thấy đầu ra kiểu Processing triggers for man-db (2.9.1-1) ..., nghĩa là trang manual cho man jamesg.blog đã sẵn sàng dùng được
      Hiện giờ chỉ mới có nội dung giữ chỗ, có lẽ ngày mai tôi sẽ hoàn thiện
      Có thể sắp thành một bài blog
  • Không cần fork hay ghi file trung gian, có thể pipe thẳng vào 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 -

    • Tốt nhất là không nên làm vậy. 2 giờ trước yrro cũng đăng thứ tương tự, và giờ cuộc tranh luận về việc pipe {curl,wget} vào lệnh lại bắt đầu
      Bạn bè thì không để bạn mình pipe stream trực tiếp vào lệnh
      https://news.ycombinator.com/item?id=39554044
  • Để tham khảo, 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 chạy được trong môi trường của tôi
    Không cần lưu file roff cục bộ

    • Có vẻ tác giả bài gốc cố tình không làm như vậy. Việc pipe lệnh hoặc nội dung lấy từ Internet thẳng vào thứ như bash thường được coi là thói quen xấu
      Cá nhân tôi thì thấy ổn. Ai hiểu ý nghĩa bảo mật thì gần như chắc chắn cũng biết cách biến đổi này, nên không cần phải chỉ ra
      Nhưng nói cho người mới thì không hay. Một ngày nào đó họ có thể dính đòn. Khi kỹ năng tăng lên, họ sẽ tự nhiên biết các tính năng như vậy, và hy vọng lúc ấy họ cũng đã học được các hệ quả đi kèm
      Không phải bài do tôi viết: https://www.seancassidy.me/dont-pipe-to-your-shell.html
    • Đáng tiếc là lệnh đó không chạy trên macOS: /usr/bin/man: illegal option -- l
      Tôi đã thử tạo một lệnh một dòng dùng pipe trên Mac nhưng liên tục gặp lỗi
      Triển khai man của macOS không có cờ -l. Tôi đã kiểm tra trang manual
    • Nếu dùng bash, bạn có thể rút ngắn vài ký tự bằng process substitution thay vì pipe
      man -l <(curl -sL -H "Accept: text/roff" https://jamesg.blog/2024/02/28/programming-projects/)
  • Nếu nói về các URL làm những thứ thú vị trong terminal, tôi từng thấy một cái trên textfiles.com
    Nó có dạng chiếu một phim hoạt hình ngắn bằng mã terminal VT100, và toàn bộ được cung cấp từ một URI duy nhất
    Trên các hệ thống hiện đại có thể đặt giới hạn tốc độ để xem
    curl --limit-rate 1000 [http://textfiles.com/sf/STARTREK/trek.vt](<http://textfiles.com/sf/STARTREK/trek.vt>;) && reset
    reset được thêm vào vì terminal có thể bị hỏng
    Các URI dựa trên terminal khác gồm curl cheat.sh/tar, lấy ví dụ sử dụng chương trình sau dấu /, và curl wttr.in/berlin, lấy thông tin thời tiết đã được định dạng cho terminal

    • Nếu muốn tự làm video ASCII bằng telnet, vài năm trước tôi có làm một thứ bằng Go: https://github.com/bfontaine/RickASCIIRoll
      Thực ra nó khá đơn giản, phần khó nhất là tạo frame
      Việc này có thể làm bằng ffmpeg+img2txt.py: https://github.com/bfontaine/RickASCIIRoll/tree/master/movie...
    • Vài năm trước tôi đã làm một trình xem ANSI art có mô phỏng tốc độ modem
      Vì có một mirror cũ của https://16colo.rs/, nên có thể xem phần lớn ANSI art đã được công khai cho đến nay
      Ví dụ: curl ansi.hrtk.in/ungenannt_1453.ans
    • Thật sự rất tuyệt, nhưng nó cũng làm terminal hỏng hoàn toàn. Vui thật
    • Cũng có Star Wars xem bằng telnet
      https://itsfoss.com/star-wars-linux/
    • Dùng tritty có thể mô phỏng tốc độ truyền 1200/9600 BPS
  • Giờ thứ cần có chỉ là một bộ chuyển đổi Markdown sang roff, mà tìm thử thì đã có sẵn rồi
    https://github.com/postmodern/kramdown-man
    https://rtomayko.github.io/ronn/ronn.1.html
    https://kristaps.bsd.lv/lowdown/

  • Có một gói Emacs cài SICP của Abelson và Sussman vào thư mục Info
    Chỉ cần nhập M-x package-install sicp RET
    Thấy vậy, tôi nghĩ cũng có thể cài cả một giá sách archive blog bằng feed reader đã chỉnh sửa
    Đọc Info trong Emacs thì cũng dùng được bookmark

    • Cài cả chicken-scheme là được. Sau đó chạy với quyền root
      chicken-install srfi-203
      chicken-install srtfi216
      ~/.csirc cho SICP như sau
      (import scheme)
      (import (srfi 203))
      (import (srfi 216))
      (define (inc x) (+ x 1))
      (define (dec x) (- x 1))
      Sau đó cứ dùng geiser user và geiser cho chicken như bình thường là được
    • Nhân tiện, SICP là sách của Abelson và Sussman
  • Có lẽ tìm trên Internet là ra câu trả lời, nhưng tôi muốn hỏi HN
    Tôi nhớ hồi trung học trên HP-UX, có ai đó đã chỉ cách nhấn một tổ hợp phím nào đó để nhảy tới một từ được gạch dưới, tức là một tham chiếu section, nhưng tôi không tài nào nhớ đó là phím gì
    Tôi đã kiểm tra cả man(1)man(7) nhưng không tìm thấy. Có thể đó là ký ức giả

    • Nếu đó là man, cần nhớ rằng man ohman về bản chất là nroff -man /usr/share/man/man1/ohman.1 | $PAGER
      Tức là bạn không tương tác với man hay nroff, mà đang tương tác với pager
      Ngày nay less là phổ biến nhất, và more thực tế cũng nhiều khả năng là less, nhưng ngày xưa còn có các thứ khác, và HPUX có thể đã dùng thứ như pg
      pg thuộc dòng AT&T, more thuộc dòng BSD, còn less thuộc dòng GNU
      Cả ba đều bắt đầu tìm kiếm regex bằng /, nên có thể tìm bất kể có gạch dưới hay không
      less cũng hỗ trợ file tag, nên có thể nhảy tới tag tiếp theo bằng t
    • Tôi không rõ có tính năng riêng của trình xem man hay không, nhưng cũng có thể bạn đang nhớ tới dthelpview, trình xem trợ giúp CDE. Nó có thể đã hiển thị man page
    • Nghe giống texinfo mở bằng lệnh info
      Trớ trêu là khá nhiều tài liệu groff gốc được viết bằng texinfo: https://lists.gnu.org/archive/html/groff/2005-10/msg00107.ht...
  • Không hiểu sao chi tiết nhỏ nhặt này lại kích hoạt bản năng bắt bẻ của tôi. Có lẽ vì ai đó trên Internet hơi sai
    Có thể vì ngay từ đầu nó đã quá thiên về Linux một cách không cần thiết, hoặc vì tôi kỳ vọng điều gì đó khác nhưng rốt cuộc lại chỉ là một demo ngắn về content negotiation của NGINX
    Dù sao thì cũng có vài điểm vô bổ mà tôi cứ muốn nói ra
    Nói nghiêm túc thì nó không trả về roff. Những thứ như .TH không phải bản thân roff, mà là một phần của gói macro để viết trang man
    Tôi hơi thất vọng vì không có chuyển đổi Markdown-to-roff. Tôi tưởng đó mới là phần thú vị của bài này, và ít nhất cũng có thể dùng một công cụ sẵn có nào đó
    Tương tự, vì vậy mà định dạng văn bản thực ra cũng không hẳn đúng. Đầu vào roff chủ ý dùng mỗi câu một dòng để phân biệt dấu . ở cuối câu với dấu . dùng cho mục đích khác
    Ngoài ra, mọi dòng bắt đầu bằng . đều có thể bị diễn giải là lệnh và gây vấn đề
    Hoặc đơn giản là tôi chỉ là một ông già khó tính

    • Cảm ơn đã chia sẻ điều này. Tôi không biết chính xác quan hệ giữa roff và man có cấu trúc ra sao, và đã sửa bài này nhiều lần để cố cho đúng
      Có những công cụ khác như groff, nroff nên lại càng rối hơn
      Chỉ riêng một bài giải thích “roff/man page/nroff/các biến thể khác là gì và dùng như thế nào” cũng đủ thành một bài blog rồi
      Nếu có một phần giải thích ngắn gọn, rõ ràng thì tôi cũng sẽ thích, và có lẽ cũng hữu ích cho người khác
      Markdown-to-roff thì tôi nghĩ để dành cho v2. Khi tôi bắt đầu cân nhắc triển khai parser, có người chỉ cho tôi https://github.com/sunaku/md2man, và có vẻ vấn đề này đã được giải quyết
      Tôi cần tìm cách tích hợp nó vào site Python của mình chạy trên GitHub Pages, nên sẽ phải chỉnh sửa đôi chút
    • Tôi cũng khá ngạc nhiên vì không có chuyển đổi Markdown-to-roff
      Pandoc có thể chuyển Markdown sang roff dạng man page rất dễ dàng
      Nếu đưa nó vào template đã cho thì trông sẽ giống một trang man thật hơn
  • Media type đúng theo RFC 4263 là text/troff: https://www.rfc-editor.org/rfc/rfc4263.html

  • Ý tưởng hay đấy. Giờ chỉ cần bấm giờ xem bao lâu nữa sẽ có “cung cấp bài blog của tôi dưới dạng DOOM WAD có thể chơi được”

    • Cứ thêm vào danh sách vài việc hay ho hiếm hoi mà AI thực sự có thể giúp được