跳至主要內容

在 Linux 安裝 TeX Live 2026

將使用 ISO 映像在 Linux(RHEL 系)上安裝 TeX Live 2026 的步驟整理如下,適用於 RHEL 系統。

前置條件

  • 具有 sudo 權限
  • 磁碟空間至少 10 GB(ISO 約 6.4 GB + 安裝目標約 8 GB)
  • 有網路連線

Step 1: 下載 ISO 映像檔

從理化學研究所的鏡像站下載 ISO。

cd
curl -C - -O --progress-bar https://ftp.riken.jp/CTAN/systems/texlive/Images/texlive2026.iso

-C - 是在下載中斷時繼續下載的選項。

下載完成後,確認檔案大小(約 6.4 GiB):

ls -lh ~/texlive2026.iso

Step 2: 掛載 ISO 映像

建立掛載點,並將 ISO 以 loop 裝置掛載為唯讀:

sudo mkdir -p /mnt/texlive
sudo mount -o loop,ro ~/texlive2026.iso /mnt/texlive

確認是否掛載:

ls /mnt/texlive

出現如下檔案即可視為成功:

install-tl archive tlpkg README ...

Step 3: 執行安裝程式

sudo /mnt/texlive/install-tl

會啟動文字介面互動選單。

主要操作按鍵

操作
S選擇安裝方案
D變更安裝目錄
I開始安裝
Q結束(取消)

建議設定

  • 方案: scheme-full(全部套件,包含日本語 LaTeX 所需的 collection-langjapanese
  • 安裝路徑: /usr/local/texlive/2026(預設)

確認設定後輸入 I 開始安裝。安裝需時數十分鐘。

Step 4: 設定 PATH

將 TeX Live 的執行檔路徑加入 ~/.bashrc

echo 'export PATH="/usr/local/texlive/2026/bin/x86_64-linux:$PATH"' >> ~/.bashrc
source ~/.bashrc

Step 5: 修正語系 (RHEL 系)

在 RHEL 系統若未設定 locale,執行 lualatex 時會發生錯誤。請執行:

sudo dnf install -y glibc-langpack-en

另外,在 shell 啟動時設定 locale:

echo 'export LANG=C.UTF-8' >> ~/.bashrc
source ~/.bashrc

Step 6: 檢查是否可正常執行

確認各指令的版本:

tex --version
lualatex --version
platex --version

預期輸出範例:

TeX 3.141592653 (TeX Live 2026)
LuaHBTeX, Version 1.24.0 (TeX Live 2026)
e-upTeX 3.141592653-p4.1.2-u2.02 (TeX Live 2026)

Step 7: 測試日文編譯

建立測試檔案:

cat > /tmp/test.tex << 'EOF'
\documentclass{jlreq}
\begin{document}
日本語のテスト。TeX Live 2026 による日本語組版のサンプルです。
\end{document}
EOF

編譯:

cd /tmp && lualatex test.tex

若生成 test.pdf 即為成功。

Step 8: 清理(可選)

安裝完成後,可刪除 ISO 與掛載點:

sudo umount /mnt/texlive
rm ~/texlive2026.iso

鏡像站列表

若下載速度慢,可嘗試其他鏡像站:

鏡像站URL
理化學研究所(建議)https://ftp.riken.jp/CTAN/systems/texlive/Images/
JAISThttps://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/Images/
山形大學https://ftp.yz.yamagata-u.ac.jp/pub/CTAN/systems/texlive/Images/
CTAN 鏡像https://mirror.ctan.org/systems/texlive/Images/

AI 代理人的「技能」是什麼?淺顯說明其運作原理

將「技能」加入 AI 代理人,就像在應用程式安裝功能擴充一樣,可以增加它能做的事情。本文解說 Agent Skills 的運作方式,以及代理人在內部如何處理任務。

AI 代理人是什麼

首先作為前提,AI 代理人是指「接受指示並能自主執行任務的 AI 程式」。

與像 ChatGPT 那種「問了就回答」的 AI 不同,代理人可以做以下事情:

  • 讀寫檔案
  • 執行程式碼並檢查結果
  • 呼叫外部 API 或工具
  • 自行判斷並執行多步驟流程

技能是什麼

Agent Skills 是一種「用來為代理人新增能力或專業知識的機制」。

比喻成人類的話,就像把「新的工作手冊」交給對方一樣。閱讀了手冊(技能)的代理人就能理解該任務應如何進行,並據此行動。

無技能: 「寫一篇部落格」→ 代理人隨便寫
有技能: 「寫一篇部落格」→ 按照手冊步驟,以一定品質寫出文章

技能主要以 Markdown 檔案(SKILL.md)描述,通常包含以下要素:

  • 步驟說明:要做哪些事、按什麼順序做
  • 腳本:想自動化的處理流程
  • 範例與設定:代理人會參考的資源

為什麼需要技能

AI 代理人能力很強,但不會預先具備你專案特有的知識

例如:

  • 「這個團隊要如何撰寫提交訊息」
  • 「這個部落格的 front matter 要如何寫」
  • 「部署流程要用哪個指令」

這類資訊如果不以技能方式提供,代理人無從得知。有了技能,代理人就能在知道「正確做法」的前提下執行任務。

代理人使用技能時的處理流程

接下來看看代理人在內部如何運作。

重點如下:

1. 載入技能

代理人一開始會載入技能。技能內容成為輸入給 LLM 的一部分(提示)。LLM 讀到後會理解「這個任務的正確做法」。

2. 任務拆解

LLM 根據收到的步驟,將任務拆成小步驟。例如「先閱讀既有文章 3 篇」「接著決定檔名」「寫 front matter」等。

3. 呼叫工具

在每個步驟中,視需要呼叫工具:讀檔、搜尋網路、執行程式碼等,依照技能定義的流程執行。

4. 結果回饋

工具執行的結果會再次傳回給 LLM。LLM 根據結果判斷「下一步要做什麼」,並持續迴圈直到任務完成。

技能指令

技能可以透過斜線指令(/命令名)來呼叫。

呼叫指令時,對應的 Markdown 檔內容會被展開為提示,代理人就會開始依該步驟執行。

技能的應用範圍

Agent Skills 的格式是 由 Anthropic 開發並開放的,目前支援許多工具。

工具是否支援
Claude Code
GitHub Copilot
Cursor
Gemini CLI
OpenAI Codex
VS Code

同一套技能能在多個工具間重複使用,這是很大的優勢。

總結

  • 技能是把專業知識或操作步驟傳給代理人的機制
  • 只要撰寫 Markdown 檔(SKILL.md)描述步驟與規則,就能建立技能
  • 代理人把技能當成提示給 LLM,LLM 會解讀並按步驟執行
  • 這是個開放且跨工具的標準格式,如 Claude Code、Cursor、GitHub Copilot 等均可共用

善用技能可以省去「每次都要向 AI 解釋同一件事」的麻煩,讓代理人以一致的品質完成任務。

在 AWS 無伺服器環境下自建部落格留言 API

想要在這個部落格加入留言功能,因此在 AWS 無伺服器架構下自建了一個 API。介紹其設計與實作。

架構

請求以以下流程處理。

瀏覽器 (www.hikari-dev.com)
↓ HTTPS
API Gateway
├── GET /comment?postId=... → 取得留言
├── POST /comment → 發表留言
└── PATCH /comment/{id} → 管理(切換隱藏)

Lambda (Node.js 20 / arm64)

DynamoDB(儲存留言)
+ SES v2(通知管理者)

程式碼以 TypeScript 撰寫,並以 SAM(Serverless Application Model)管理基礎設施即程式碼(IaC)。Lambda 採用 arm64(Graviton2)以稍微節省成本。

DynamoDB 的資料表設計

資料表名稱為 blog-comments,分區鍵為 postId,排序鍵為 commentId

型別說明
postIdString文章的識別值(例: /blog/2026/03/20/hime
commentIdStringULID(可時序排序的 ID)

因為在排序鍵使用 ULID,使用 QueryCommand 取得的留言會自動按發表順序排列。這也是為什麼沒有使用 UUID 的原因。

垃圾留言過濾

在將留言寫入 DynamoDB 之前,會與 keywords.json 中定義的關鍵字比對。

若命中關鍵字,會以 isHidden: true 自動隱藏,並附加 isFlagged: "1"。若未命中則立即公開。

isFlagged 作為稀疏 GSI(Sparse GSI)的鍵使用。未命中的留言不會帶入此屬性,因此不會在 GSI 中增加多餘的分區,對成本與效能都有利。只要在 DynamoDB Document Client 設定 removeUndefinedValues: true 就能達成。

通知管理者的郵件

每次有留言發表時,會用 SES v2 發信通知自己。郵件內容包含發言者名稱、內容、評分、IP 位址以及是否被標記(flag)等資訊。

郵件發送採非同步進行,即使發送失敗也會忽略錯誤(不影響使用者流程)。這是為了避免影響留言發表的回應速度。

隱私考量

雖然會在 DynamoDB 中儲存 IP 位址與 User-Agent,但不會把它們包含在 GET 端點的回應中。我們在型別定義層就進行分離。

安全性

層級對策
網路使用 AWS WAF 設定每個 IP 每 5 分鐘 100 次的速率限制
CORS僅允許 https://www.hikari-dev.com
管理 APIAPI Gateway 的 API Key 認證(X-Api-Key 標頭)
垃圾留言關鍵字過濾自動隱藏

管理端點(PATCH /comment/{id})只要在 SAM 模板設定 ApiKeyRequired: true 即可啟用 API 金鑰認證。無需自行實作 Lambda Authorizer,較為簡單。

總結

採用無伺服器架構後不需管理伺服器,DynamoDB 的按需付費讓流量低的個人部落格也能把成本降到最低。

程式碼以 SAM + TypeScript + esbuild 打包,僅需執行 sam build && sam deploy 即可部署。

製作可使用多個生成式 AI 代理人的 VSCode 聊天擴充套件 Hime

我做了一個可以和多個 AI 供應商聊天的 VSCode 擴充套件 Hime (HikariMessage)

採用 BYOK(Bring Your Own Key),只要有各 API 提供者的 API 金鑰即可使用。

什麼是 Hime

Hime 是一個整合在 VSCode 側邊欄的生成式 AI 聊天擴充套件。支援 Anthropic、OpenAI、Azure OpenAI、OpenRouter、Ollama,並可透過下拉選單輕鬆切換提供者。

主要功能

支援多個 AI 提供者

支援以下提供者:

  • Anthropic (Claude)
  • OpenAI
  • Azure OpenAI
  • OpenRouter
  • Ollama

即時串流顯示

回應會即時串流顯示,因此即使回覆很長也不會感覺到太久的等待。

MCP

透過設定以 JSON 格式指定以下參數即可使用 MCP。

範例

{
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users"]
}
}

豐富的 UI

  • Markdown 呈現
  • 程式碼區塊語法高亮
  • 程式碼區塊複製按鈕
  • 顯示 MCP 的輸出結果

聊天記錄持久化

對話記錄會以 JSON 格式儲存在 ~/.hime/chats/。即使重新啟動 VSCode 也能從上次繼續對話。

自動系統提示

工作區資訊、作業系統資訊與目前開啟的編輯器上下文會自動包含在系統提示中。只要說「請修正這個檔案」,AI 就能知道你正在查看哪個檔案。

安裝

需要 Node.js 20 以上與 VSCode 1.96 以上。

git clone https://github.com/Himeyama/hime
cd hime
npm install
npm run watch # 開發時: Extension Host 與 Webview 同時監控

之後在 VSCode 按 F5 即可啟動擴充套件主機。API 金鑰可從側邊欄的設定面板輸入,並會使用 VSCode 的 SecretStorage 加密儲存。

總結

Hime 的強項是在不離開編輯器的情況下即可與 AI 互動,並且可透過 MCP 執行工具。歡迎試用。

程式碼倉庫: https://github.com/Himeyama/hime

用 AWS 無伺服器打造雲端儲存服務

はじめに

自分専用のファイル共有システムが欲しいと思い、AWS のサーバーレスサービスだけでファイルストレージサービスを作りました。

この記事では、設計で意識したポイントと、実際のアーキテクチャを紹介します。

何を作ったか

制作した Web システムは、Web ブラウザからファイルのアップロード・ダウンロード・フォルダ管理ができるクラウドストレージサービスです。

主な機能

  • ファイルのアップロード / ダウンロード
  • フォルダの作成・階層管理
  • 複数ファイル / フォルダの一括 ZIP ダウンロード
  • ユーザー認証(サインアップ・ログイン・パスワードリセット)
  • ユーザープロフィール管理

アーキテクチャ

以下、構成図です。

認証の大部分は Cognito で行い、 ファイル転送は S3 の Presigned URL を Lambda で発行してクライアントと S3 が直接やり取りする仕組みです。

技術スタック

レイヤー技術
バックエンドC# (.NET 8) / AWS Lambda
認証Amazon Cognito + Managed Login v2
APIAPI Gateway (REST) + Cognito Authorizer
ストレージAmazon S3

設計判断とその理由

認証を Cognito で行う

Cognito の OAuth 2.0 エンドポイントと Managed Login を活用し、認証機能を実現しました。

最終的に認証系の Lambda は TokenFunction 1 つだけになりました。

機能的にもセキュリティ的にも減らせるコードは減らすのが吉です。

AWS のサービスがやってくれることを自前で書く必要はありません。

Presigned URL によるファイル転送

ファイルのアップロード・ダウンロードで Lambda を経由すると、いくつかの問題が生じます:

  • Lambda のペイロード上限に引っかかる
  • 大きなファイルを Lambda のメモリに載せるとコストがかかる
  • 転送時間が Lambda の実行時間としてカウントされる

Presigned URL なら、Lambda は URL を発行するだけで、実際のファイル転送はブラウザと S3 が直接行います。

Lambda の実行時間は数十ミリ秒で済み、ファイルサイズの制約も S3 の上限までとなります。

アップロードの流れ:
1. ブラウザ → Lambda: 「file.pdf をアップロードしたい!アップロード先の URL を送れ~」
2. Lambda → ブラウザ: 「アップロード先の Presigned URL だよ。ここに PUT してね~」
3. ブラウザ → S3: 「S3 に PUT するよ~」
4. ブラウザ → Lambda: 「アップロード完了したよ~」

4. フォルダの ZIP ダウンロード

S3 にはフォルダごとダウンロードする機能がありません。

複数ファイルの一括ダウンロードは、Lambda 上で ZIP を生成して一時的に S3 に置き、その Presigned URL を返す方式にしました。

一時 ZIP ファイルは S3 のライフサイクルルールで 1 日後に自動削除されるので、ゴミが溜まることはありません。

セキュリティ

対策実装
ブルートフォース防止Cognito 標準のロック機能 (5 回失敗: 15 分ロック)
API 保護Cognito Authorizer による JWT 検証
CORSAllowedOrigin を特定のドメインに限定
一時ファイル管理S3 ライフサイクルで不要なファイルを 1 日で自動削除

コスト

サーバーレス構成なので、利用がなければコストはほぼゼロです。

  • Cognito: ESSENTIALS Tier は MAU 10,000 まで無料
  • Lambda: 月 100 万リクエストまで無料
  • S3: 保存量に応じた従量課金(GB あたり約 $0.025/月)
  • API Gateway: 100 万リクエストあたり $3.50

個人利用なら月額数十円〜数百円程度に収まります。

インフラのコード化

インフラ全体を 1 つの template.yaml (AWS SAM) で定義しています。

Cognito User Pool、API Gateway、Lambda 3 関数、S3 バケット、CloudWatch アラーム、SNS — すべてのリソースを 600 行程度の YAML で定義しています。

在 WSL 2 上安裝 Rocky Linux 8.10

下載 WSL 2 映像檔

https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-Container-Base.latest.x86_64.tar.xz

從這裡下載。

參考: https://docs.rockylinux.org/8/guides/interoperability/import_rocky_to_wsl/

解壓映像檔

將 tar.xz 解壓成 tar 檔。轉成 tar 後即可匯入到 WSL2。

cd ~/Downloads

xz -d Rocky-8-Container-Base.latest.x86_64.tar.xz

※Windows 上預裝的 bsdtar 無法解壓此檔。
※若沒有 xz 指令,可在 Cygwin64 安裝,或使用其他 WSL 發行版來解壓。

匯入映像至 WSL2

將映像匯入到 WSL2。

wsl --import RockyLinux-8.10 $HOME .\Rocky-8-Container-Base.latest.x86_64.tar --version 2

確認映像

wsl -l -v

新增使用者並設定預設使用者

以使用者名稱 hikari 為例:

wsl -d RockyLinux-8.10 -u root -- dnf install sudo passwd -y

wsl -d RockyLinux-8.10 -u root -- adduser hikari

wsl -d RockyLinux-8.10 -u root -- passwd -d hikari

wsl -d RockyLinux-8.10 -u root -- usermod -aG wheel hikari

wsl -d RockyLinux-8.10 -u root -- sed -i 's/^# %wheel/%wheel/' /etc/sudoers

wsl -d RockyLinux-8.10 -u root -- echo -e "[user]\\ndefault=hikari" `| tee -a /etc/wsl.conf

啟動映像

wsl -d RockyLinux-8.10

關於最強壓縮方式的調查

結論

先說結論。

調查了對總計 34 GiB 的大量檔案進行壓縮與解壓時的時間與大小。

壓縮・建立封存檔

方式指令realusersys壓縮後大小
tartar cf large-pkg.tar large-pkg1m19.449s0m6.702s0m48.121s26 GiB
tar.gztar czf large-pkg.tar.gz large-pkg11m33.942s11m18.811s0m55.573s5.5 GiB
LZ4tar cf - large-pkg | lz4 > large-pkg.tar.lz43m33.187s0m53.958s2m57.122s8.6 GiB
zstdtar cf - large-pkg | zstd -T0 -o large-pkg.tar.zst9m13.819s1m45.049s2m43.609s4.8 GiB
bzip2tar cf - large-pkg.1 | bzip2 > large-pkg.tar.bz237m22.743s28m40.329s3m31.000s4.3 GiB
xztar cf - large-pkg | xz > large-pkg.tar.xz125m31.447s124m10.523s5m18.330s3.3 GiB

展開 (解壓)

方式指令realusersys
tartar xf large-pkg.tar2m11.793s0m6.906s2m4.183s
tar.gztar xf large-pkg.tar.gz3m39.544s2m1.189s2m58.317s
tar.gz(gzip)gzip -dc large-pkg.tar.gz | tar xf -3m40.416s2m0.272s3m0.043s
tar.gz(pigz)pigz -dc large-pkg.tar.gz | tar xf -3m53.711s1m38.147s4m42.893s
LZ4lz4 -dc large-pkg.tar.lz4 | tar xf -4m46.576s0m32.174s4m36.055s
zstdzstd -dc large-pkg.tar.zst | tar xf -3m46.419s0m46.533s3m34.668s
bzip2bzip2 -dc large-pkg.tar.bz2 | tar xf -11m31.287s9m52.644s4m17.974s
xzxz -dc large-pkg.tar.xz | tar xf -8m11.527s3m45.562s7m15.109s

準備大量小型檔案

首先,作為大量小型檔案,使用 node_modules。

package.json 設成如下。套件隨意挑選。

package.json
{
"name": "large-pkg",
"version": "1.0.0",
"description": "",
"author": "",
"type": "commonjs",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"async": "^3.2.6",
"axios": "^1.13.2",
"bcryptjs": "^3.0.3",
"bluebird": "^3.7.2",
"body-parser": "^2.2.2",
"chalk": "^5.6.2",
"chalk-template": "^1.1.2",
"cheerio": "^1.1.2",
"chokidar": "^5.0.0",
"commander": "^14.0.2",
"cookie": "^1.1.1",
"core-js": "^3.47.0",
"cors": "^2.8.5",
"debug": "^4.4.3",
"dotenv": "^17.2.3",
"express": "^5.2.1",
"fast-glob": "^3.3.3",
"form-data": "^4.0.5",
"glob": "^13.0.0",
"got": "^14.6.6",
"inquirer": "^13.2.0",
"jsonwebtoken": "^9.0.3",
"lodash": "^4.17.21",
"mime": "^4.1.0",
"minimist": "^1.2.8",
"mkdirp": "^3.0.1",
"mkdirp-classic": "^0.5.3",
"mongoose": "^9.1.4",
"ms": "^2.1.3",
"node-fetch": "^3.3.2",
"ora": "^9.0.0",
"passport": "^0.7.0",
"prop-types": "^15.8.1",
"qs": "^6.14.1",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"request": "^2.88.2",
"rimraf": "^6.1.2",
"semver": "^7.7.3",
"sharp": "^0.34.5",
"socket.io": "^4.8.3",
"supports-color": "^10.2.2",
"tslib": "^2.8.1",
"uuid": "^13.0.0",
"ws": "^8.19.0",
"xml2js": "^0.6.2",
"yargs": "^18.0.0"
},
"devDependencies": {
"@babel/cli": "^7.28.6",
"@babel/plugin-transform-runtime": "^7.28.5",
"@babel/preset-env": "^7.28.6",
"@babel/runtime": "^7.28.6",
"autoprefixer": "^10.4.23",
"ava": "^6.4.1",
"babel-core": "^6.26.3",
"babel-loader": "^10.0.0",
"chai": "^6.2.2",
"commitlint": "^20.3.1",
"concurrently": "^9.2.1",
"conventional-changelog": "^7.1.1",
"cross-env": "^10.1.0",
"css-loader": "^7.1.2",
"dotenv-expand": "^12.0.3",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react": "^7.37.5",
"husky": "^9.1.7",
"jest": "^30.2.0",
"less": "^4.5.1",
"lint-staged": "^16.2.7",
"mocha": "^11.7.5",
"nodemon": "^3.1.11",
"playwright": "^1.57.0",
"pm2": "^6.0.14",
"postcss": "^8.5.6",
"prettier": "^3.8.0",
"puppeteer": "^24.35.0",
"rollup": "^4.55.1",
"rxjs": "^7.8.2",
"sass": "^1.97.2",
"semantic-release": "^25.0.2",
"sinon": "^21.0.1",
"style-loader": "^4.0.0",
"stylus": "^0.64.0",
"supertest": "^7.2.2",
"tailwindcss": "^4.1.18",
"ts-loader": "^9.5.4",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vitest": "^4.0.17",
"webpack": "^5.104.1",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.3",
"zx": "^8.8.5"
}
}

接著用以下指令建立 node_modules。

npm i

來看看容量。

$ du -h -d1
566M ./node_modules
567M .

這樣就知道已建立大量小型檔案。

複製 node_modules 以建立更多檔案。

for i in {1..59}; do
echo "node_modules.${i} をコピー中..."
cp -r node_modules "node_modules.${i}"
done

再看一次容量。

$ du -h -d1
...
34G .

變成相當大的容量。

tarball

看看製作 tarball 的速度。

建立檔案

$ time tar cf large-pkg.tar large-pkg

real 1m19.449s
user 0m6.702s
sys 0m48.121s

建立後 26 GiB

解壓

$ time tar xf large-pkg.tar

real 2m11.793s
user 0m6.906s
sys 2m4.183s

打包與解壓都算快。

tar.gz

接著用 tar.gz 測試。

tar 指令是用來打包的命令,gzip 是壓縮單一檔案的命令。把兩者結合所產生的就是 tar.gz 檔。現在 tar 指令本身就能壓縮與解壓 tar.gz(其他壓縮格式也同理)。

壓縮

$ time tar czf large-pkg.tar.gz large-pkg

real 11m33.942s
user 11m18.811s
sys 0m55.573s

壓縮後:5.5 GiB

解壓

$ time tar xf large-pkg.tar.gz

real 3m39.544s
user 2m1.189s
sys 2m58.317s

用 tar 與 gzip 解壓

$ time sh -c 'gzip -dc large-pkg.tar.gz | tar xf -'

real 3m40.416s
user 2m0.272s
sys 3m0.043s

速度幾乎沒差。

並行解壓 (pigz)

$ time sh -c 'pigz -dc large-pkg.tar.gz | tar xf -'

real 3m53.711s
user 1m38.147s
sys 4m42.893s

速度沒有太大改變。看起來檔案讀寫速度成為瓶頸。

bzip2

壓縮

$ time sh -c 'tar cf - large-pkg.1 | bzip2 > large-pkg.tar.bz2'

real 37m22.743s
user 28m40.329s
sys 3m31.000s

壓縮後:4.3 GiB

壓縮花了比較多時間,但壓縮率還不錯。

解壓

$ time sh -c 'bzip2 -dc large-pkg.tar.bz2 | tar xf -'

real 11m31.287s
user 9m52.644s
sys 4m17.974s

解壓也花不少時間,但解壓率還不錯。

xz

壓縮

$ time sh -c 'tar cf - large-pkg | xz > large-pkg.tar.xz'

real 125m31.447s
user 124m10.523s
sys 5m18.330s

壓縮後:3.3 GiB

壓縮率極為優秀,但花的時間太多。

如果網路極度慢、儲存成本高、且多年才用一次的罕見情境,或許可以考慮。

解壓

$ time sh -c 'xz -dc large-pkg.tar.xz | tar xf -'

real 8m11.527s
user 3m45.562s
sys 7m15.109s

LZ4

壓縮

$ time sh -c 'tar cf - large-pkg | lz4 > large-pkg.tar.lz4'

real 3m33.187s
user 0m53.958s
sys 2m57.122s

解壓

$ time sh -c 'lz4 -dc large-pkg.tar.lz4 | tar xf -'

real 4m46.576s
user 0m32.174s
sys 4m36.055s

zstd

Meta(前 Facebook)開發的高速壓縮方式。

壓縮

$ time sh -c 'tar cf - large-pkg | zstd -T0 -o large-pkg.tar.zst'
/*stdin*\ : 18.57% (27492075520 => 5106239218 bytes, large-pkg.tar.zst)

real 9m13.819s
user 1m45.049s
sys 2m43.609s

解壓

$ time sh -c 'zstd -dc large-pkg.tar.zst | tar xf -'
large-pkg.tar.zst : 27492075520 bytes

real 3m46.419s
user 0m46.533s
sys 3m34.668s

Libertouch ES(日文版)評測

Libertouch ES

我入手了 Libertouch ES 的日文配列 (NC07902-B281-ES)。

不客氣地說,使用約一個月後的感想。

優點

  • 打字手感
    在薄膜式鍵盤中屬於頂級。很貼合手指,有類似機械式的輕盈感,但觸感又不是機械式那種回饋。希望能一直堅持薄膜設計。
  • 堅固
    鋁製很堅固,幾乎可以當鈍器使用。
  • 鍵帽與 Cherry MX 相容
    能更換鍵帽這點很棒。

希望改善的地方

  • 有時候按鍵不被識別…
    有些鍵按下去沒有反應,有時似乎還會出現多個鍵互換的情況。應該是韌體或軟體的問題。
  • 鍵帽容易脫落…
    可能是結構上的問題,空白鍵和 Enter 鍵特別容易掉,感覺不太穩定。
  • 希望有替換用鍵帽
    雖然有提供鍵位修改軟體這點不錯,但還是希望能有替換鍵帽。尤其左下的 Windows 鍵需求應該很大,特別想要。若能有 Home、End 也更好。
  • 希望更換或附加附贈線材
    附贈的是 Type-C to C 線。但電腦多數仍是 Type-A,所以希望能附 Type-A to C 線。
  • 價格偏高
    因為是試作品,8 萬日圓的高價可以理解。如果把所有問題都解決,售價落在 3 萬日圓左右我會考慮購買。若高於那個價格會比較難決定。

總評

Libertouch ES 是一款在打字感與堅固性表現優異的高品質薄膜鍵盤。但在實用面仍有需改進之處,例如輸入識別不穩、鍵帽易脫落、附贈線材規格等。如果能增加替換鍵帽與線材選項,會更具吸引力。希望未來能有全尺寸或 80% 的選擇,這類需求可能比 65% 更大。

期待後續的改良與正式發售。

在 Raspberry Pi 上使用 mkcert 為 cockpit 建立證書,並設定到伺服器(cockpit)與瀏覽器的方法

執行環境

以下環境確認過可用:

  • Raspberry Pi 5
  • AlmaLinux

各證書的意義

  • raspberrypi.pem: 伺服器證書(公鑰)
    這個證書是針對主機名稱 raspberrypi 所簽發的 SSL 證書。像是網頁瀏覽器等用戶端會用它來驗證伺服器的正當性。(安裝於伺服器)
  • raspberrypi+1.pem: 伺服器證書(公鑰)
    這個證書是針對 raspberrypi <IP 位址> 這類主機名稱所簽發的 SSL 證書。同上
  • raspberrypi-key.pem:
    raspberrypi.pem 對應的私鑰。由伺服器端持有,用於 SSL 通信的加解密。絕對不可外洩。(安裝於伺服器)
  • raspberrypi+1-key.pem:
    raspberrypi+1.pem 對應的私鑰。由伺服器端持有,用於 SSL 通信的加解密。絕對不可外洩。同上
  • rootCA.pem: 本地根憑證(公鑰)
    mkcert 自動產生的本地 CA(憑證機構)憑證。將此憑證安裝到用戶端(例如瀏覽器)後,即可將 raspberrypi.pem 視為可被信任的憑證。
  • rootCA-key.pem: 本地 CA 的私鑰
    rootCA.pem 對應的私鑰,供 mkcert 用來簽署伺服器證書(例如 raspberrypi.pem)。這是 mkcert 內部使用的,通常不需要手動操作。

證書的產生

使用 mkcert 指令來產生證書。產生後部署到伺服器。

mkcert raspberrypi <IP アドレス>

sudo cp raspberrypi+1-key.pem /etc/cockpit/ws-certs.d/raspberrypi.key
sudo cp raspberrypi+1.pem /etc/cockpit/ws-certs.d/raspberrypi.crt
sudo systemctl restart cockpit

確認本地根憑證的位置

確認要安裝到電腦的根 CA 憑證所在位置。

mkcert -CAROOT

將根憑證複製到 PC

將根憑證複製到電腦。

scp raspberrypi:/home/<USER>/.local/share/mkcert/rootCA.pem .
cp rootCA.pem rootCA.cer

在 Windows 上註冊憑證

開啟 rootCA.cer,在憑證存放區將憑證新增到「受信任的根憑證機構」。

在 Android 裝置上註冊憑證

rootCA.pem 移到裝置,然後從設定中安裝註冊。

如何用 podman 安裝 OpenStreetMap

匯入

請將 japan-xxx.osm.bpf 下載到家目錄。 接著執行以下指令做準備。volume 選項末尾的 :Z 是在 SELinux 運作時要加上的。

請不要更改 /data/region.osm.pbf 的部分。

podman volume create osm-data

podman run -v <ダウンロードした osm.bpf ファイル>:/data/region.osm.pbf:Z -v osm-data:/data/database/ overv/openstreetmap-tile-server import

匯入範例

podman volume create osm-data

podman run -v <ダウンロードした osm.bpf ファイル>:/data/region.osm.pbf:Z -v osm-data:/data/database/ overv/openstreetmap-tile-server import

執行

使用以下指令啟動圖磚伺服器。

podman run -p 8080:80 -v osm-data:/data/database/ -v osm-tiles:/data/tiles/ -d overv/openstreetmap-tile-server run

設定防火牆後,網路上的其他裝置即可存取。

備份

podman volume export osm-data > osm-data.tar