惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
月光博客
月光博客
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
O
OpenAI News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Know Your Adversary
Know Your Adversary
Last Week in AI
Last Week in AI
S
Securelist
Engineering at Meta
Engineering at Meta
博客园 - 司徒正美
P
Privacy & Cybersecurity Law Blog
T
Tailwind CSS Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
U
Unit 42
Microsoft Azure Blog
Microsoft Azure Blog
Hacker News: Ask HN
Hacker News: Ask HN
Hugging Face - Blog
Hugging Face - Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
P
Proofpoint News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
Lohrmann on Cybersecurity
IT之家
IT之家
Security Archives - TechRepublic
Security Archives - TechRepublic
I
InfoQ
S
Security @ Cisco Blogs
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Full Disclosure
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
人人都是产品经理
人人都是产品经理
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
B
Blog RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research

Random Thoughts

LLM 训练与推理的基本理解 昨日的世界不再重来 Magic Brush - 画出你自己的产品宇宙 写在 AI Coding 奇点之后 尼泊尔布恩山小环线纪行 2025 投资组合年报 三十而笠 我的 8 年职业生涯回顾 我的颈椎病康复之旅 - 关于选择的故事 熊野古道中边路纪行 新加坡旅居三年再回首 高加索三国 - 阿塞拜疆行记 2024 投资组合年报 重新思考 Go:了解程序在线上是如何运行的 欧游散记 —— 环勃朗峰之旅 欧游散记 —— 特摩索斯古城 重新思考 Go:Channel 不是「消息队列」 重新思考 Go:Slice 只是「操作视图」 那一天,我决定踏出一步 True Story 少数价值 A Snapshot of Myself - 2021 RPC 漫谈: 连接问题 RPC 漫谈:序列化问题 RPC 漫谈: 限流问题 科学,技术与工程 Pond: Golang 通用对象池 Golang for-range 内部实现 Golang Interface 内部实现 真理的有限性 Golang rand 库锁竞争优化 Meaningless or Meaningful 分布式文件系统的演化 从动物森友会聊主机游戏联机机制 创业公司的文化 伊朗见闻录 NodeJS 内存泄漏检测与定位 设计实现高性能本地内存缓存 Travel Map Linux I/O 栈浅析 SSD 背后的奥秘 什么是真正的编程能力 沉默与反沉默的理由 自由意志下的选择 学习思维方式而非学习观点 从程序到人 —— 情头配对助手的前世今生 即刻多端实时通信实践 朝鲜 —— 小国寡民的主体思想实践 使用 Surge 提升多网络环境下的流畅开发体验 一份其实好吃的 LaTeX 入门餐 Kafka 的设计与实践思考 欧游散记 —— 民主专制下的德国 欧游散记 —— 维也纳 欧游散记 —— 西班牙 欧游散记 —— 德国国会大厦 社会矛盾讨论统一框架的一种可能性 欧游散记 —— 伪君子布拉格 Lemon : Koa 风格的 Python 异步 Web 框架 Golang : Make Programming Happy Again Podcast 闲言碎语 Kubernetes 中使用 API Gateway 替代 Ingress ElasticSearch 最佳实践 blog.joway.io Presentations
命令行里的设计艺术
Joway · 2019-01-11 · via Random Thoughts

在谈论手机 App 或是网页时,我们总会谈到交互设计,但倘若涉及到面向专业用户的 CLI(Command-line interface)领域,很少有人会将它与用户交互相联系。甚至在很多人的大脑里,已经把 CLI 和「难用」画上了等号,更有甚者认为 CLI 的难用才体现了其 「专业性」。

与此同时,「RTFM」(Read The Fucking Manual) 作为一个梗在工程师群体广泛流传,也让许多作者对于其不好用的 CLI 有了一个天然的借口。

虽然我们都知道阅读文档是一个好习惯,但恐怕大部分人在用一个新命令前都不会去仔细阅读完它的文档手册,就算你今天读了,你不可能永远记得,也不可能每次记忆模凌两可的时候都去重读一遍。所以可想而知一个高度依赖文档用户才能够正常使用的 CLI 不是一个合格的 CLI。

以下是我对于命令行设计的一些个人观点与观察,所涉并不一定广泛和正确,仅作为抛砖引玉,也欢迎在评论里提出你的看法。

遵循约定俗成

例如 mv, cp, ln 这些命令都遵循 [action] [source_file] [target_file] 的格式,这不一定很有逻辑,但既然都已经约定俗成了,如果你违犯这个顺序要倒过来,其实是一件蛮缺德的事情。为什么说「缺德」?因为一个用过了你的命令的人,他很容易开始怀疑是不是还有别的命令行打破了这个约定,他会对其它命令也不放心,最后他甚至会忘了真正被广泛采用的约定用法是什么了,导致每次用类似语法的命令都胆颤心惊,这点我深有体会 😔。

所以如果你的命令有类似的约定俗成可以遵守,你应该遵守业内的这种约定,这是一种职业道德。

一致的命令组织结构

对所有和用户打交道的产品来说,「一致性」是天条。我遇到过两种被广泛采用的组织风格:

$ [cmd] [module] [flags] [args]

$ [cmd] [action] [flags] [args]

无论是按模块划分还是按行为划分本质思路其实都是一样的,有些人会认为项目大了会难以遵守这套规范,但即便是目前规模已经非常庞大的 kubectl,它依旧坚持以 [cmd] [action] [flags] [args] 为基础的设计准则。在有些较为复杂的地方,它可以用 subcmd 来进一步向外部隐藏复杂性 [cmd] [subcmd] [action] [flags] [args],例如:

$ kubectl config [action] [flags] [args]

我几乎每天都会用到 kubectl,但我的确很少去看它的文档,甚至我都想不到我是怎么就会使用它的。越是在复杂的项目面前,这种一致性带来的好处越明显。

而一个典型的反面教材是 Git。它是我用过的最复杂同时又是最混乱的 CLI,以一般人最常用的分支操作举例:

# create branch
$ git checkout -b new_branch

# delete branch
$ git branch -D new_branch

你会发现对于创建和删除分支这种最基本的命令,它 checkout 是一个动词,branch 又是一个名词,前者 -b 来代表 branch 这个名词,后者 -D 来代表 delete 这个动词。短短两行命令,还是最常用的两行就让我们看到这种设计是多么没有逻辑。更别说还有 git pullgit fetch 这种了。

面向用户而非面向实现

对于 Git 的设计,它的拥护者会声称如果从 Git 底层实现的视角来看这种设计才能够明白它的用意,但这种辩解隐藏的一个意思是,使用者不仅要读使用文档,还要去钻研 Git 的底层实现,这样才能够顺利使用这个工具。

对于 CLI 工具,尤其是 Git 这类注定会被大规模用户长时间使用的工具,指望所有人都能够弄懂它的底层实现是不现实的。出于对用户负责的角度,更应当将底层抽象成一个更容易被人理解的模型,面向这个模型去封装底层实现。而不是反过来借用你的命令行设计让用户去理解底层实现。Git 或许还有这个资格要求用户去学习,其它命令行可能就没这种底气了。

繁琐胜过歧义

在歧义这点上最让我痛苦的是 mysql 的命令:

$ mysql -u username -p password -P prot

$ ssh -p port
$ redis-cli -p port -a password

可以看到对于其它很多 CLI,-p 指的都是 port,但是在 mysql 里 -P 才是 port,虽然 mysql 有它自己的理由,但这种设计实实在在给用户造成了很大的困扰,像 redis 就选择用 -a 来取代 -p 作为 password,给 port 腾出位置。

甚至于我比较激进地认为,不应当推荐使用大写的缩写方式,我们记住 -p 是依赖于 port 的首字母,但是对于大小写我们没有可以辅助联想记忆的地方,非常容易搞混。

所以相比与为了偷懒搞那么多缩写,我更愿意使用能够联想记忆的全拼参数。比如为了防止出现上面 port 和 password 两个参数不一致的情况,我现在经常是手动打全 --port 的,对我来说,按回车的心情舒畅比提心吊胆地用缩写要来得划算许多。

安全第一

如果你的命令会造成一些不可逆的行为,在设计时应该首先把安全性放在第一位。假设一个命令 wm 是把一张图片作为水印打在另一张图片上,并且会覆盖原图,那么我就不会设计成这种形式:

$ wm [watermark_file] [source_file]

在上面的形式里,你想象的语义是 watermark [watermark_file] on [source_file],但用户想的可能是 watermark [source_file] with [watermark_file]。你不能确保这个,即便是所谓的约定俗成用法也没法给予你这个保障。所以我会偏向于采用这样参数的设计:

$ wm --texture [watermark_file] [source_file]

这样用户看到这个命令就应该明白是怎么个处理,按下回车的时候心情能够愉悦许多。