logo一言堂

三个命令行工具

UNIX命令行的美丽之处是虽然每个工具只做一件事,但把它们组合起来,你可以完成所有复杂的工作。下面介绍的三个工具可能不太出名,做的事情其实也有点复杂,无法用简单几句话说清,而且感觉和UNIX的哲学也有点背离,但对于程序员和系统管理员的日常工作确实非常有用。我这里一并介绍,希望能给大家以启发。

stow

如果你像我一样,手动编译安装不少软件,肯定会和我有一样的烦恼。这就是安装容易,卸载难。在一个UNIX环境中,所有执行文件,所有库,等等都是混在一起放的,你很难分清楚什么文件是什么源包安装的,所以并没有统一的,干净的卸载流程。有时候你甚至觉得可能像 OS X 一样,所有安装文件完全分开才简单.但一起放有一起放的好处:路径设置简单.如果路径过于复杂,那先后顺序也很有影响,你也很难发现各个软件包之间的有没有冲突.stow 这个工具解决的就是多软件包安装卸载的问题,你为什么不可以同时得到分开放和一起放的好处呢?通过管理 symlink, stow 把多个目录下的文件形式上合并在一起,并可以随时拆除更换.下面举例:

packageA
   |-> bin		
      |-> commandA
   |-> lib
      |-> libA

packageB
   |-> bin		
      |-> commandB
   |-> lib
      |-> libB

合并之后成为:

local
   |-> bin
      |-> commandA -> ../stow/packageA/bin/commandA
	  |-> commandB -> ../stow/packageB/bin/commandB
   |-> lib
      |-> libA -> ../stow/packageA/lib/libA
	  |-> libB -> ../stow/packageB/lib/libB

正如传统 UNIX 的扁平目录组织形式一样.而且还有其他:

  • 扁平目录里只有 symlink, 并不占什么磁盘空间
  • 提供工具分 package 拆除/更换/检验 symlink
  • 自动检查冲突

事实上,这个工具不仅可以用于管理软件安装,任何你又想分目录管理又想扁平管理的地方都可以应用它.我已经开始用它管理文档,图片,音乐等等.现代的文件系统在一个目录里放几万个文件毫无问题,所以尽情把它们 stow 到一起吧,你会获得短路径,易搜索,好遍历的好处,同时不放弃分项目管理的干净整洁.

ack

我想人人都用过 grep. grep 来自 UNIX 的哲学,只做一件事情,就是字符串匹配.但任何尝试过在一个不是很简单的目录结构下 grep 过的人都和我一样受过伤害.你先来:

find . | xargs grep derek

发现没有跳过目录.然后你再来:

find . -type f | xargs grep derek

然后你意识到有些路径名包含空格,所以你再来:

find . -print0 -type f | xargs -0 grep derek

然后你找到一大堆二进制文件,只好重来:

find . -print0 -type f -name "*.c" | xargs -0 grep derek

然后你意识到还有 .h 文件,.cpp 文件,... 还有假如某些脚本并无固定后缀呢?快崩溃了吧?

当然一些老手会说,你可以 git grep. 但如果你 grep 的目录没有用 git 管理呢?但如果你想一起 grep 多个 git repo 呢?

ack 是 grep 在 DWIM (Do What I Mean) 哲学下的重新实现. 你只需要:

ack derek
ack --perl derek
ack --noperl derek

你猜猜看上述命令都干了啥?简单总结一下 ack 的好处:

  • 自动跳过你其实不想 grep 的文件和目录
  • 普通人可以记住理解的命令行
  • 高亮显示匹配结果
  • 集成到 emacs 和其他编辑器之内

parallel

这个年代每个人的电脑都是多核的.但可惜的是很多任务其实只能利用一个CPU. 如果你一个一个地去做,即使写成脚本,也是大大浪费时间.例如你有一堆图片,需要转换成另一个分辨率,有或者你有一堆 regression test 等着去做,你当然想并行地去做不是?parallel 是一个并行多任务 scheduler, 它有以下好处:

  • 自动识别你有多少CPU, 来达到最好的效率
  • 自动缓存打印信息,这样在并行运行的时候屏幕打印仍貌似串行运行的一样可读
  • 当某个任务异常退出的时候,不再提交新的任务但让已提交的任务正常运行到结束
  • 清晰合理的命令行语法
  • 还有很多很多高级功能

下面我给一个简单范例是我常常使用的:

ls -d * | parallel -j+0 make -C {}

它的含义是:对于当前目录下所有子目录,每一个都进入运行 make, 并发数受限于 CPU 的个数.怎么样?让电脑满负荷运行吧,我自己要回家睡觉了.

总结

以上三个命令行工具其实都有十年以上的历史了,算的上久经考验.它们的目的并非直截了当,但都是程序员为了解决自己的痛点,为自己和同行自发设计的工具,从一个非常简单的开端逐渐演变到一个相对复杂和完备的现状,没有任何商业背景.

最后,它们都是用 perl 写的哦.

Make the easy things easy and the hard things possible. -- Larry Wall