点文件 -- 开发第一课
develop
在 github 上搜索 dotfiles 可以找到至少二十万的代码仓库。 甚至有人“暴论”点文件是学校没教的重要一课。 那么,什么是点文件呢?
注册表 v.s. 纯文本格式配置文件
在 Unix 刚刚问世的时候,利用利用纯文本作为软件配置方式的思路就被定下来了。例如许多 POSIX 兼容的系统中会有一个目录 /etc
(甚至 Windows 也有 C:\Windows\System32\Drivers\etc
)用来存储纯文本格式配置文件 ( editable text configuration )。在这之后 Windows 3.0 才引入了注册表,通过二进制文件的数据库来存储软件的配置,同时提供一个图形化用户界面的软件(注册表编辑器)来修改软件的配置。
- 纯文本格式配置文件
- 方便:可以使用任何文本编辑器直接编辑,并通过语言服务器对配置文件的编辑提供辅助,例如 json, yaml, toml 格式的配置文件可以使用 json schema , xml 格式的配置文件可以使用 XML Schema Definition 或 Document type definition, python/lua/perl 等脚本语言有专门的语言服务器。
- 可以添加注释说明某些修改的原因,方便后期维护
- 可以使用 git 备份
- 比数据库慢
- 注册表
- 必须使用专门的软件进行编辑。例如:
- Windows 的
regedit
- Gnome 的
dconf
- Xfce 的
xfconf
- Windows 的
- 不可以添加注释
- git 备份后也无法通过 diff 看到每次改动二进制文件后的变化
- 快
- 必须使用专门的软件进行编辑。例如:
早期 Windows 引入注册表是有进步意义的:那时 Unix 运行在性能更好的大型机上,而 Windows 运行的性能较弱的微机。然而随着技术的进步,注册表的优势被削弱,而劣势则暴露无疑。目前, GNU/Linux 除了 gtk 系的一些桌面在用数据库外,几乎所有软件都采用纯文本格式的配置文件。即便是微软开发的 powershell 和 VS Code 也使用纯文本格式的配置文件。毕竟,比起下图中的代码补全,又有谁愿意去打开根本记不住的 HKEY 开头的注册表路径去配置软件呢?
从点文件到标准化
作为一名开发人员,你很可能在你的整个职业生涯都要使用并改进你的点文件,点文件很可能是你从事过最长的项目。
– Anish Athalye Managing Your Dotfiles
Unix 将配置文件分为 2 类,一类是系统管理员 ( root 用户)编辑的 /etc
,是所有用户共用的。另一类是每个用户专属的配置文件。用户的配置文件比所有用户共用的配置文件拥有更高的优先级。早期这些文件被放在家目录下,以点开头,故称点文件。 Unix 会隐藏点开头的文件所以平时这些文件不会被看到。
随着时间的推移,各个平台都开始通过标准来确定配置文件的存储路径。
- GNU/Linux
-
~/.local/share/my_app
,/usr/share/my_app
放共享资源 -
~/.cache/my_app
,/var/cache/my_app
放缓存 -
~/.config/my_app
,/etc/my_app
放配置
-
- Android
-
/data/data/com.my_app/files
放共享资源 -
/data/data/com.my_app/caches
放缓存 -
/data/data/com.my_app/shared_prefs
放配置
-
- macOS
-
~/Library/Application Support/my_app
放共享资源 -
~/Library/Caches/my_app
放缓存
-
- Windows
-
C:\Users\%USERNAME%\AppData\Local\my_app
放不需要同步的文件 -
C:\Users\%USERNAME%\AppData\Roaming\my_app
放需要云端同步的文件
-
- Windows XP
-
C:\Documents and Settings\%USERNAME%\AppData
含有空格所以后来被换成了Users
-
例如 GNU/Linux 采用 Freedesktop 的 XDG 标准,将原来的 ~/.my_app
分成多个不同的目录分别存储共享资源、缓存、配置。这样的好处是:
- 不会污染保持家目录:让用户可以自行管理家目录而不用担心误操作破坏破坏某些点文件
- 分类管理
- 用户存储空间不够可以删掉
~/.cache
- 用户可以直接将
~/.config
用 git 备份
- 用户存储空间不够可以删掉
但并不是所以平台都分得这么细,例如在 Windows 上软件就不得不从 C:\Users\%USERNAME%\AppData\Local\my_app
自行分出几个子目录用来分别存储共享资源、缓存、配置,或者另创建 C:\Users\%USERNAME%\AppData\Local\my_app-data
来存储共享资源等。
所以从引入 XDG 标准后,原先的备份 ~/.my_app
就变成了备份 ~/.config/my_app
了。因为 .config
仍是以点开头,所以通常不加区分地统称点文件。
值得注意的是 XDG 标准定义了一些路径用来存储特定文件:
-
~/.local/share/fonts/
: 字体 -
~/.local/share/backgrounds/
: 桌面壁纸 -
~/.local/share/sounds/
: 铃声 -
~/.local/share/themes/
: 桌面环境的主题 -
~/.local/share/applications/
: 快捷方式 -
~/.cache/thumbnails/
: 缩略图,这个目录下所有XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.png
的文件名均是缩略图对应的原始图片路径的 hash 。这个设计比 Windows 的Thumbs.db
好,因为后者将缩略图文件放在与原始图片相同的目录下,想象以下用户将一个目录下的某几张图片删了,但忘记删除缩略图文件就直接将整个目录压缩发给另一个用户了,收到压缩包的用户完全可以通过缩略图文件看到被删除图片的一些信息,这样就造成了信息的泄漏。可以说 XDG 将缩略图单独放在一个目录下的设计是一个非常高明的设计。 -
~/.config/autostart/
: 自启动 -
~/.config/user-dirs.dirs
: 描述桌面、文档、下载等是~/Desktop
,~/Documents
,~/Downloads
等的配置文件。 类似 Windows 下的~/Desktop/desktop.ini
,~/Documents/desktop.ini
,~/Downloads/desktop.ini
等。 -
~/.config/user-dirs.locale
: 描述语言。默认是en_US
。 -
~/.config/mimeapps.list
: 描述文件的默认打开方式。
这意味着软件不能把名字命名为 fonts
, backgrounds
等等。
开发者开发软件时要遵守每一个平台的标准,但记住每个平台下的标准很麻烦。所以许多语言都能找到支持这些标准的库。
被点文件配置的常见软件类别
使用不同语言、不同软件的开发者点文件往往差异很大。按照 https://wiki.archlinux.org/title/Dotfiles#User_repositories , 包括以下类别:
- 外壳 ( Shell )
- 桌面环境或窗口管理器
- 编辑器
- 终端模拟器
- 终端复用器
- 声音播放器
- 进程监视器
- 邮件用户客户端
- 即时通信软件
- 文件管理器
- RSS 阅读器
每类软件单独拿出来介绍都足以构成一篇长篇大论。例如:
- 编辑器的作用是编辑文本
- 有哪些可供选择的编辑器,他们分别对编辑器开发史上提出了哪些贡献:
- vi 第一个引入多模式编辑
- emacs 第一个允许用户通过编写脚本 (Lisp) 配置编辑器
- vim 继承了 vi 的多模式编辑,还允许用户通过图灵完备的脚本语言 (vim script) 来配置编辑器,并诞生了 LSP 的前身 YCM
- text mate 引入 tmLanguage 通过数据描述语言来描述语法高亮
- Sublime 第一个引入多光标编辑的模式,并改进 tmLanguage 成 sublime-syntax
- Atom 第一个引入增量式语法高亮系统 tree-sitter
- VS Code 统一了 LSP 标准
- neovim 用 LuaJIT 改善了 vim script 的性能,并成为早期集成 tree-sitter 和 LSP 的编辑器之一
- 然后又得介绍上面出现的所有新概念,介绍怎么开发对应的插件和软件……
不再赘述。如有需要请自行搜索
常见的需要配置的部分
REPL 主题
许多语言的 REPL 默认的提示符都非常丑陋。 用户通常希望自定义提示符来显示当前平台的种类、当前所在的路径、时间、 git 信息等。 在这些语言中, shell 是 REPL 使用最频繁的语言。有很多用户喜爱自定义他们的 shell 。 一些主题封装了自定义的代码并允许用户进一步配置某些细节。笔者个人非常看重可配置性。
笔者一开始在点文件中写了一些功能,后来发觉代码量开始变多,而且可能对其他人有用就单独提出来封装成一个个 zsh/tmux/vim 插件了。
zsh
笔者默认的 shell 是 zsh。 powerlevel10k 是目前性能最好的 zsh 主题,其获取 git 信息的部分用 C++ 实现从而在性能上成功超越了之前的 powerlevel9k 。
还有很多 REPL 笔者并没有找到很好的主题,甚至只好自己动手实现了一些轮子。
bash
笔者一般交互用 zsh ,但日常写 shell 脚本还是用 bash 。
- bash-prompt: 一个 bash 主题。
tcl
也是一种 shell 。几乎是 EDA 行业的标准配置语言。
- tclreadline: 为 tcl 默认的 REPL 添加了 readline 的快捷键支持
-
tcl-prompt: tclreadline 主题,也提供了一些用 tcl 编写的软件的 REPL 主题:
-
tk
的wish
expect
-
vivado
的vivado -mode tcl
-
vitis
的xsct
-
python
其实笔者一般用 ptpython
或者与 ipython
兼容但性能更快的 ptipython
。
octave
matlab 的开源解释器。
- prompt: octave 主题
perl
有一说一, perl 做数据驱动编程真不错。
-
reply: 一个比 perl 默认 REPL
perl -de0
更好的 REPL - Reply-Plugin-Prompt: 一个 Reply 主题
lua
- luaprompt: 一个比 lua 默认 REPL 更好的 REPL
-
prompt-style.lua: luaprompt 主题。因为 lua 经常作为一种“寄生型语言”寄生在很多软件中,所以也提供了对应的 REPL :
- neovim
- luatex
- pandoc
- neomutt
translate-shell
translate-shell: 笔者嫌弃现在能找到的命令行翻译软件颜值太低时随手写的软件。
高能预警。
gdb
嗯, gdb 也是一个 REPL !
gdb-prompt: 一个 gdb 主题
lftp
还有一些 REPL 目前没有提供自定义提示符的功能:
状态栏
一些软件的状态栏也是可以自定义的:
(neo)vim
- vim-airline: 一个 vim 的状态栏主题。 Bram Moolenaar 去世后,该插件的作者接管了 vim
- 除了 airline 默认的插件外,笔者只添加了 电池插件、无线网插件、时钟插件
tmux
- tmux-status-bar: 笔者非常反感很多 tmux 主题极低的可配置性所以写了这个插件。
- 上方那个显示实验室的 GPU 数量的插件 也是笔者写的。不过整个实验室竟然没有别的人用?
- 将阿拉伯数字转换为圆圈里的数字的插件也是笔者写的~
- 还出现了其他番茄钟插件、电池电量插件等
色彩高亮
高亮代码
- lesspipe: 高亮 less 输出的结果
- manpager: 高亮 man 输出的结果
-
zsh-help: 高亮
--help
输出的结果 -
zsh-colorize-functions: 高亮
functions
输出的结果 - bat: 被以上程序调用
高亮命令
- colorize: 高亮许多命令输出的结果
- grc: 被 colorize 调用
-
vivid: 高亮
ls
等软件的输出结果 -
eza:
ls
和tree
的有图标版。 一些替代品例如lsd
有严重的性能问题
高亮命令行输入
-
fast-syntax-highlighting: 高亮 zsh 命令行的输入。这个高亮非常厉害,如果 perl 语法不正确,那么
perl -e 'wrong_code'
中的wrong_code
会被直接高亮为红色,如果语法正确才会被高亮为蓝色。
高亮补全
- fzf-tab
- fzf: 被 fzf-tab 调用
- fzf-tab-source: fzf-tab 的预览源的一个收集
补全
- zsh-completions: zsh 的 第三方补全库
- zsh-bash-completions-fallback: 让 zsh 在没有找到某程序对应的补全脚本的时候使用 bash 的补全
- bash-completion: bash 的 第三方补全库
- zsh-completions-for-cross-compilers: 拿 gcc 的补全来给 mingw 之类的交叉编译器用
每种 shell 的补全语法各不相同。一般是软件开发者都是用一些库来自动生成补全的脚本:
- shtab: 为 python 的 argparse 生成补全脚本
- argopt: 可以和 shtab 一起为 python 的 docopt 生成补全脚本
- omelette: 为 javascript 生成补全脚本
- clap_complete: 为 rust 的 clap 生成补全脚本
- cobra: 为 go 生成补全脚本
有些语言没找到类似的库就直接自己造轮子吧……
杂项
-
undollar: 提供命令
$
和%
来防止从 stackoverflow 上复制的代码不能运行
点文件管理
为什么我希望将我的点文件放在 GitHub 上?
- 备份、恢复和同步您的首选项和设置工具箱。您的点文件可能是计算机上最重要的文件。
- 向社区学习。发现适合您工具箱的新工具和那些你已经使用过的新技巧。
- 与我们其他人分享您所学到的知识。
最常见的管理方式就是 git 。例如笔者平时把点文件托管在 github 上的一个仓库里,每当有换新电脑时就会执行如下操作:
cd
git clone --depth=1 --bare https://github.com/Freed-Wu/Freed-Wu
git config core.bare false
git reset --hard
一个窍门是 github 允许用与自己用户名相同的仓库作为 github 用户主页的 profile 。所以我们可以利用这一特性将点文件的仓库名设置为用户名,从而让点文件的 README 在 github 用户主页被展示。
一些其他的管理方法见 https://wiki.archlinux.org/title/Dotfiles#Tools 。对 Nix 用户,home manager 也是个不错的方法。
笔者倾向于把所有平台的点文件放在一起。例如笔者的点文件里其实包含几个平台家目录下的点文件:
- GNU/Linux
- Android termux
- Windows Msys2
对于不同发行版的 /etc
下的不同配置,笔者倾向于在不同的仓库分别管理,因为不同发行版可能会使用不同的镜像,配置等。
联动
Github codespace
Github 支持用点文件自定义代码空间。例如笔者自己用点文件定义的代码空间:
gh cs create -RFreed-Wu/Freed-Wu -mbasicLinux32gb -dwzy
# wait about 3 minutes to install needed programs and zsh/vim/tmux plugins
gh cs ssh
Docker
docker pull freedwu/Freed-Wu:main
docker run --rm -it --name=test --gpus=all -p 8022:22 freedwu/Freed-Wu:main
结果类似 Github 代码空间。