Posts archived in CompSci

待月西厢下,迎风户半开。隔墙花影动,疑是玉人来。

最近 twitter 上最流行的一个关键词是”西厢计划”. 这个计划名字取得很浪漫,客户端叫做张生,对,就是西厢记里面那个翻墙去见崔莺莺小姐的张生;显然,服务器端必然叫做崔莺莺。客户端的张生是最重要的部件,可以不依赖于服务端工作。

我是个特别好奇的人,遇到好玩的总要学习一下看看是怎么弄的。因为西厢计划的作者只是简要的介绍了一下原理,其他报道又语焉不详,我当时就觉得很好奇,花了昨天一个晚上详细读了一下源代码,终于知道怎么回事了,觉得原理非常漂亮,所以写篇文章介绍总结一下。

先说大方向。大家都知道,连接被重置的本质,是因为收到了破坏连接的一个 TCP Reset 包。以前剑桥大学有人实验过,客户端和服务器都忽略 Reset, 则通信可以不受影响。但是这个方法其实只有理论价值,因为绝大多数服务器都不可能忽略 Reset 的 (比如 Linux, 需要 root 权限配置iptables, 而且这本身也把正常的 Reset 给忽略了)。只要服务器不忽略 Reset, 客户端再怎么弄都没用,因为服务器会停止发送数据,Reset 这条连接。所以,很多报道说西厢计划是忽略 Reset, 我从源代码来看应该不是这样。在我看来,西厢计划是利用了墙的一个可能的弱点–墙只在连接发起的时候把一个 TCP 连接加入监听序列,如果墙认为这个连接终止了,就会从监听序列中去掉这条记录,这样,这条连接上后续的包就不会被监听。西厢计划就是让墙“认为”这个连接终止的一个绝妙的方法。只要墙认为这个连接两端都是死老虎,墙就不会触发关键词检测,其后所有的数据,都不存在连接被重置的问题了。

如何让一个连接置之死地而后生,就是西厢计划那帮黑客神奇的地方了。这也不是一日之功。 首先,这帮牛人发现,墙的是一个入侵检测系统,把含有关键字的包当成一种“入侵”来对待。采取这种设计有很多好处,但缺点是入侵检测系统可能具有的问题,墙都可能有。西厢计划主页上那篇著名的论文就是讲这些七七八八的漏洞的。可以说处理这些七七八八的漏洞是非常困难的,迫使墙的设计者“拆东墙,补西墙”。这样补来补去,外表看起来好像很牛逼的墙,其实有很多本质上无法简单修补的漏洞,其中有一个致命的,就是 TCP 连接状态的判定问题。 出于入侵检测系统这种设计的局限,墙没有,也没办法准确判定一条 TCP 连接的状态,而只是根据两边收到的数据来“推测”连接的状态。而所有的关键词检测功能,都是基于“连接还活着”的这个推测的结果的。因为墙的规则是在连接发起的时候开始对这条连接的检测,在连接终止的时候停止对这条连接的检测,所以,一旦对连接的状态推测错误,把还活着的连接当成已经关闭的连接,墙就会放弃对这条连接上随后所有的包的检测,他们都会都透明的穿过墙的入侵检测。

上面只是想法,具体到 TCP 协议实现这一层,就要只迷惑墙,还不能触及我要通信的服务器。最理想的情况下,在任何有效通信之前,就能让墙出现错误判断,这些,就需要对 TCP 协议有深刻理解了。西厢计划的那帮黑客,居然真的去读 TCP 几百页的 RFC,还居然就发现了方法(这里我假设读者都知道 TCP 的三次握手过程和序列号每次加一的规则)。 我们都知道,三次握手的时候,在收到服务器的 SYN/ACK 的时候,客户端如果发送 ACK 并且序列号+1 就算建立连接了,但是客户端如果发送一个序列号没 +1 的 FIN (表示连接终止,但是服务器知道,这时候连接还没建立呢, FIN 这个包状态是错的,加上序列号也是错的,服务器自己一判断,就知道这个包是坏包,按照标准协议,服务器随手丢弃了这个包), 但这个包,过墙的时候,在墙看来,是表示连接终止的(墙是 ma de in china, 是比较山寨的,不维护连接状态,并且,墙并没有记下刚才服务器出去的 SYN/ACK 的序列号,所以墙不知道序列号错了)。所以,墙很高兴的理解为连接终止,舒了一口气去重置其他连接了, 而这个连接,就成了僵尸,墙不管你客户端了,而这时候,好戏才刚刚开始。

事实上,墙是双向检测的(或者说对每个包都检测的),因此,对服务器和客户端实现相同的对待方法,所以,墙不管客户端还不行,假如服务端有关键词传给客户端,墙还是有可能要发飙的(这里说有可能,因为我也不知道)。所以,最好的办法就是,让服务端也给墙一个终止连接的标志就好了。可是这个说起来简单,做起来难,怎么能让不受自己控制的服务器发一个自己想要的包呢? 西厢计划的那帮黑客,再次去读几百页的 RFC, 令人惊讶的发现,他们居然在 RFC 上发现了一个可以用的特性。我们上面说了,三次握手的时候,在收到 SYN/ACK 后,客户端要给服务器发送一个序列号+1 的ACK,可是,假如我不+1呢,直接发 ACK 包给服务器。 墙已经认为你客户端是死老虎了,不理你了,不知道你搞什么飞机,让这个 ACK 过了。可是服务器一看,不对啊,你给我的不是我期待的那个序列号, RFC 上说了,TCP 包如果序列号错了的话,就回复一个 Reset. 所以,服务器就回复了一个 Reset。这个 Reset 过墙的时候,墙一看乐了,服务器也终止连接了,好吧,两边都是死老虎了,我就不监听这条连接了。而至于客户端,这个服务器过来的 Reset 非常好识别,忽略就是。随后,客户端开始正确的发送 ACK, 至此,三次握手成功,真正的好戏开始,而墙则认为客户端和服务器都是死老虎,直接放过。所以,张生就这样透明的过了墙。 至于过墙以后所有的事情,《西厢记》里面都有记载,各位读者自行买书学习。

现在的西厢计划客户端,即“张生”模块的防连接重置的原理就是这样,服务器端,即莺莺模块的实现也是类似的。防DNS那个,不懂 DNS 协议,所以看不懂。我猜想,因为开发人员都是黑客,所以自然喜欢用最经得起折腾和高度定制的 Linux 开发。 现在看西厢计划的实现,因为依赖于 Linux 内核模块 netfilter, 在 Linux 上如鱼得水,但往其他平台的移植可能是个亟待解决的问题。 我觉得,在其他平台上,可以通过 libpcap 和 libnet ,在用户态实现相同的功能,就是有点麻烦而已,有兴趣的懂网络的可以照西厢计划原理,在家自行做出此功能;当然,全中国人民都用 Linux 最好 :)

PS 1: 据说是西厢计划一个作者画的原理图:http://img.ly/DIi
PS 2: 我对 TCP 的理解仅限于课本,如果上面的对技术的理解有错,请大家指出。
PS 3: 有些漏洞,可能是设计上本质缺陷,不是那么容易修复的。
PS 4: 除了最后一个图,本文没有其他相关链接,如需相关资料,自行 Google。

以前我在博客里说过我用 Gtalk 控制我家的空调,这样我在回家之前就可以先开空调, 到家的时候正好家里凉爽起来了。 这个技术很简单, 只需要弄一个所谓的 X10 设备就行了。 我的朋友 Bao Sheng 做了一个视频, 有兴趣的可以看看他的演示和讲解。

X10 是一种非常方便的协议, 所有的控制信号都通过家中的电源线传播, 所以设备一插到插座上, 就可以被控制,不需要另外布线, 即插即用,对于我这种租房的人来说非常方便。 X10 设备有很多控制的方式。 有一种 X10 设备, 可以连接在计算机上, 然后计算机就可以发送指令控制其他X10设备。 计算机和X10 之间的接口是串口,所以如果你觉得不爽,用单片机和一个 MAX232 芯片就可以控制了,不需要庞大的计算机。

如果家中的电灯等不是插在插座上,而是通过墙上开关控制,你只需要将家中原有的电灯开关换成支持 X10 的电灯开关就行了。这种 X10 的电灯开关, 和普通墙上开关大小一样,功能一样, 但在 eBay 上的售价比在 Home Depot 买的普通开关还便宜,所以完全可以把家中开关全部换掉。这样就可以用计算机控制家中原有的电灯。

空调, 电饭锅, 风扇, 电视机等电器都可以如此控制。 至于我说的网络控制,就更加简单了, Gtalk 协议都是公开的,只需要写个客户端挂机监听就行了。 如果想玩这套东西的, 我这里有源代码。 X10 还有无线射频接口, 所以可以在车上控制车库的门或者门口的灯, 在家也可以躺在床上一键关灯,都是无线接口能干的事情,这里面的技术,和电动车锁的原理是一样的,你甚至可以把家里面的锁换成车上那种,一有人撬,立即杀猪似的发出警报。

控制电器有很多应用, 比如看电影的时候,能够达到一键打开电视和DVD以及关掉大灯的效果。早晨也可以通过控制灯慢慢亮起来让人自然醒来。至于控制空调,电饭锅这些,都是完全看需要看想象力了。

以上这些玩意,属于第一代智能家庭,我家基本上都部署了。 我现在考虑把我们家往二代智能家庭进化, 具体来说要能做到以下几点, 有些技术细节我没想好, 所以写出来请各位读者大牛出主意。

1. 在计算机上可视化控制一切电器

这个不难,只要写一个 GUI 客户端就行了。 如果想做得比较好玩一点,可以像  Second Life 一样, 把家中做成一个虚拟现实系统, 人走到哪个房间, 哪个房间灯自动点亮。 这个需要一个人的定位机制, 而且人身上的传感器要很小, 便于携带。 这个目前还没想好怎么弄。 大家帮我出出主意? 虚拟现实有什么好的软件? 是不是在门口装个摄像头做一下 Motion Detection 比较好?

2. 语音控制

我想语音控制所有的家电。 我看到别人 hack 了一个无线对讲机,让这个对讲机的信号送到计算机里面,但是随身放一个对讲机好像太庞大了,目前正在考虑弄一个蓝牙耳机,这个蓝牙耳机和计算机连接起来,做语音识别,然后发送信号到家电。 计算机端的语音识别软件很多的,应该很好弄。

3. 大脑直接控制和其他控制

BCI 技术还不成熟, 不过带一两个传感器的检测眼球运动的东西早就有了,如果要控制的东西不复杂,可以使用 Ocz Nia,  如果要复杂一点, 可以使用 Emotiv. 唯一的不好就是要带个帽子到处走,倒没有一个随身的车钥匙大小的遥控器方便。 自从有了 WII, 我们可以 Hack 一个 WII Remote, 然后在家挥一下 WII Remote, 就可以让灯亮起来。 要是 WII Remote 再小一点,或者自己买一个加速传感器和位置传感器粘在筷子上, 就很有魔棒的感觉了,到时候对着灯一点,灯就亮了, 哈里波特保证拜你为师 :)

4. 传感器通信

我家现在卧室的门上面都装了一个红外发射器(垃圾堆上捡了个电视遥控器,用了里面的红外发光二极管)和一个接收器,人进入屋子的时候会切断红外线一次,我就自动打开屋子里的灯,本来是贴在地上的, 可是我们家的猫老是走来走去,干扰系统,所以我不得不放在半人高的地方,可是这些器件总是要拖很长的线才能把信号送回去,我现在放半人高的地方,线拖下来, 猫就老在我接出去的线上面蹭痒,搞得系统三天两头挂掉。 各位知道有没有什么便宜的无线协议,能够把这些小器件之间的无线通信问题解决? 市面上的 WIFI 传感器都要大几十美元一个,不够便宜,这些小器件走 X10 也不行,我家没那么多插口, 而且我还得投资 TTL 到 X10 的接口。 蓝牙和WIFI 模块都太贵,RF 技术我是白痴,而且也不知道怎么做一个不互相干扰的系统,各位有高人指点指点?

5. 微型化计算机

我最近找了一些不带风扇的,可以跑 Linux 的作为家庭媒体存储中心和控制中心的计算机, 我的要求是体积要小,耗能要低,可以放在客厅或者锁在锁在壁橱里面。 其实一个几十块钱的二手计算机就可以干这些事情了,但是噪音太大,长得难看,实在不适合放在客厅。

还有就是接口,至少要支持一些 USB 口,这样我外面才能接其他的外设。 我找了一顿没找到什么好的。 各位有什么建议? 我目前找到的包括 WD My Book World Edition, 中科梦龙盒子(太贵), Foxboard, 和 Marvell Plug Computer. 或者自己 DIY 一个 ATOM 芯片的机器, 或者买一个 ARM 开发版, 或者 Hack 一个  20 块钱的二手 TiVo. 我现在还在这些技术之间纠结,想要找性价比最好的。

同时我还想搞一块很节能的液晶面板,带触摸的,算是家庭控制中枢,可以看到所有的灯和传感器的工作情况,就是不知道有便宜的小触摸面板没有?

我的目标是建立一个智能的,让我安居乐业的家。 各位读着还有什么奇妙的想法?

前几篇我已经提到了完全用命令行工作的妙处。 我提到了聊天用的 Freetalk, 查邮件用的 Mutt 等等。这篇完结篇中,我把以前没提到的零零碎碎的完全基于命令行/键盘的用法写出来.

在苹果下, 我用的 Launcher 自然是 Quicksilver. 在 Linux 下, 我一般是直接用命令行. 偶尔用用 GNOME Go, 不过基本上还是用命令行.

窗口管理器上, 我现在用的是 awesome windows manager. 这个窗口管理器支持多屏幕的纯键盘操作, 我强烈建议天天只用 Firefox 和一个编辑器一个命令行的程序员们尝试一下这个窗口管理器。 这个窗口管理器还有一个好处,就是所有的窗口控件都可以脚本化,所以,可以随意在状态栏加时间状态啊,按钮啊, 等等。 而且这些按钮啊,状态阿,都是可以在运行时用脚本修改的。比如你可以定时让状态栏闪动,只需要在 cron 脚本里面写一条就行了, 让控制闪动的脚本按时运行。

现在我聊天还是用 Freetalk. Freetalk 有一个非常好的特征,就是可以任意的加插件。 我写了一个插件,让我在工作的时候,除了爸妈,未婚妻或老板给我发消息触发一个“滴”,其他消息一概不响。这样非常集中精力。 有时候不得不上 MSN, 我就用 centerim 和 centerim-utf8 (后者支持汉字), 不过我极少用,一个主要原因就是我不知道怎么写插件。 这些不让我 hack 的软件基本上都不是太顺手的。

除了写程序和发邮件聊天,剩下的就是上网了。 自然,我上网是 Firefox。 我用了一个叫做 vimperator 的插件,可以纯键盘的用 vim 的方式控制 Firefox. 这个插件可配置性极强,基本上日常用到的翻页,开关标签和搜索,都可以一键完成。 如果您是 vim 的超级粉丝,这个插件肯定让你觉得上网冲浪从来没这么爽过。 以前我常常用 ubiquity, 不过现在鼠标被我拔了,所以”选中一段文字”这样的操作我基本不用,所以 ubiquity 用的相对少了。

还有就是看 pdf 了,我要推荐一个中国人开发的软件,叫做 apvlv。 这个软件可以让你用vim 的方式看 pdf. 比如光标往下走,直接按 j 就行了, 非常方便。 我现在看 pdf 基本上就用这个了, 每次文件另存为的时候只要 :w, 实在是大大提高效率。

最后,我在文本字符下面也发现了一个类似于 awesome 的咚咚, 叫做 screenwm. 这个软件, 可以让你横着或者竖着切分屏幕成任意的组合, 在每一个小单元里面,相当于运行着一个 GNU Screen 窗口。 所以,在我的宽屏上,我的左边是 vim, 右上面是 mutt/gtalk/todo, 右下面是 shell, 工作起来不要反复切窗口, 非常舒服。

如果您有什么完全用键盘工作提高效率的妙招,不妨留言告诉我 :)

第一眼看到这个美剧的时候, 还以为是胡编乱造的民科, 像 CSI 和 Gray 一样不靠谱瞎编. 看了开头几集之后发现: 数学细节几乎没有错的. 加上好莱坞原有的犯罪片编剧的水平, 这可比其他美剧好看多了. (而且还有这么 l33t 的名字 :)

以开头的几集为例. 第一季第一集是一个真实的数学建模. 数据, 假设各方面要素都全了. 第二集是有点扯淡的测不准(只会影响微观测量), 但是顺便看到扫雷问题是 NP 完全的时候我还是会心一笑. (顺带八卦一下, Knuth 大爷说了, NP 和 P 的问题, 会在 2048 2^11 年或者 4096 2^12 年被解决 :) 这哥们黑板上写了 CLIQUE3SAT, 不知道大家注意到没有, 呵呵.

第三集的 Patient Zero 理论还是很靠谱的, SIR 模型列在黑板上的常微分方程也是看得我心痒痒的真想当场列一个 logistic 方程(SARS刚过去的那年我参加数学建模竞赛用的数学模型就是 SIR). 就是生物信息那边是极其不靠谱的. 病毒DNA测序之后哪有能拿DNA的N级结构直接 diff 一下就能比较出病毒不同的, 那还要基因挖掘和生物信息学干啥. 不过把数学家英明神武的塑造成一个生物信息学家还算可以接受. 第四集其实没啥技术含量, 说白了也可以说非线性方程的特性不能仅从坐标维度上的信息刻画. 比如 z = xy 在 (0, 0) 点的形态是非线性的, 但是沿两个坐标轴导数都是0 (八卦一下, 我去年很长一段时间的研究就是从子空间的性态去刻画非线性函数在全空间的性态, 显然需要很多的假设才能刻画准确). 第五集有点小民科, 因为黎曼猜想的证明方法可能是代数数论或者代数几何, 不见得就能“分解任意质数”. 况且, 要是有了任意分解质数的, 就 NP=P 了, Chariles 就可以直接回家享福了 (我可能是错的, 因为分解质数好像不是 NPC). Anyway, 分解质数必然会给 NP=P 问题更多的洞见.

下面的我还没时间看. 就转了一圈. wikipedia 有专门的每集用到的数学的列表.  还有无数的博客在分析这个剧集, 或者做科普. 可贵的是, 来自科研一线的数学家在帮助设计规划这个剧集. 据说当初一开始就是 Caltech 的数学家和研究生设计的脚本, 连黑板上的公式都是他们写的. 现在 Wolfram 接手了. Wolfram Research 据说还专门派了一个小组到哥伦比亚广播公司作剧集的数学顾问. 象牙塔里面的数学系也没闲着, 西东北大学有专门的关于剧集的博客. 康乃尔大学也有关于此的数学博客. 连 TI 都有面向教育的教案.

其实这也不是个案了. 我几个月前买了一本书, 叫做 “The Physics of Superheroes” (超人背后的物理学) 文风优美, 讲解深刻, 而且写作的教授可不是民科, 而是正儿八经的明尼苏达大学的物理学教授 James Kakalios.

一线的科学家能够放下身段找些大家感兴趣的话题作科普, 这个国家的下一代怎么能不强大? 什么叫科学技术强国, 从一些小侧面就看出来了.

补: 既然说到了科普, 最近关于爱科普的文章引起的争议也很多, 补几句废话吧.

首先, 我从来就不认为方舟子的科普写的好, 或者原创性高. 不信? 读读这篇. 你要是问我方舟子一天一篇从鳄鱼到链霉素哪来这么多材料(让我一天一篇写计算机科普我都要X尽人亡啊), 我就回答了: 呵呵, 呵呵, 美国的国家地理杂志恰好很多次都比方舟子的文章早一个月两个月印刷出来送到我手里. 所以我觉得, 大家不必迷信他的科普. 他囫囵吞枣的写很多不是他的研究方向的东西, 并且脱离科研一线很多年, 写出来的东西, 看了启发是不大的. 不必迷信这个人的文章.

其次, 我也是不怎么同意连岳在地震的时候的言论的, 但是我依然敬佩他作为一个评论人的勇气和洞见. 这些洞见并不因为他没学过概率论就失去价值. 我就算知道高深的数学, 我还是没他在社会问题上深刻. 人和人是有差距的, 不要用英俊的科学脸庞去鄙视别人, 至少读这篇博的人, 我希望没有这样的不健康的心态.

英文中哲学叫做 philosophy, 中文叫做爱智慧. 智慧是个抽象的东西, 只有去爱它, 才会变得有趣. 爱科学, 或者说, 爱这个世界和社会, 科学和社会才会变得有趣. 如果一个科普工作者不爱这个世界, 哪里会有超人的物理学这样有趣的书问世呢?

和世界上大多数国际机场一样, 美国夏威夷国际机场非常大. 为了方便旅客在航站之间转运, 航站之间用巴士提供交通服务. 在夏威夷, 他们用本地人的语言把这种巴士命名为 wiki wiki, 意思是”很快”. 因为在本地人的语言里面, wiki 是”快”的意思. 

1995 年的时候, “极限编程”方法论大牛, Ward Cunningham, 觉得应该建立一个公共的网站, 让人能够输入一个 Pattern 的名字, 就能查阅到一个 Design Pattern 的用法, 而且这个网站还能被人编辑, 实现知识共享. 从此, 世界上第一个 wiki 网站就建立起来了, 他把它的东西叫做 WikiWikiWeb, 意思就是”快速查阅的网站”. 这时候 wiki 还只是在程序员之间流行, 直到 2001 年, 一个叫 Jimmy Wales 的, 创建了 Wikipedia, 从此, 才算是普及了. Wiki 和 Wikipedia 彻底改变了我们的生活. 试想, 人类协作创造了一本共享智慧的, 随时可访问(中国大陆和朝鲜除外)的百科全书, 是多么值得荣耀的伟大成就! 

且慢, 以前人类难道没有百科全书么? 有, 几乎每个像样的图书馆都有大英百科全书, 为什么这些百科全书没有如此大的改变我们的信息获取方式呢? 沿着同样的逻辑链条, 我们可以问更多的问题: 在没有 Google 之前, 似乎搜索引擎也有, 但这个东西我们很少用, 也很少听说, 为什么就是 Google 一出现, 就彻底改变了我们的检索方式呢? 

问题的答案很多, 我说我的答案: 很多事情, 只有在人能很快的完成的时候, 才有了做的可能. 这句话可能比较拗口, 反过来说可能更加好懂: 如果用某种方法做一件事情太耗时间了, 那么人就不可能用这个方法做事情. 只有一个方法能够让人足够快的做好事情的时候, 这个方法才会变得实用, 同时这个事情才有做的可能性. 

为避免过于抽象, 我们仍然用例子说明. 美国宪法规定要10年做一次人口普查, 但是直到 1890 年, 美国才进行了历史上第一次完整的全国人口普查. 传统上, 人口普查的数据提取上来, 要花多于10年的时间才能处理完, 因此, 人口普查从来就做不完. 直到1890年左右, IBM 公司发明打孔卡片, 卖给了美国人口统计局, 美国人口统计局采用了打孔卡片作为报表, 从此才能在2年内做完一次人口普查统计. 打孔卡片比人填表统计快多少呢, 也就快5倍而已. 但是就这个5倍, 把原本不可能做到的全国人口普查变成了可能. 

天气预报也是很好的例子. 天气预报的原理是解一个数值偏微分方程, 这个道理科学家在1922年就知道了. 但在计算机没出现前, 是没有天气预报员这个职业的. 直到1955年, 在电子计算机的帮助下, 天气预报才变成了现实. 那么, 1955 年的电子比1922年的机械式计算机快了多少倍呢? 也就1000倍. 另外有一个未经证实传说说, 以前预报24小时内的天气预报需要计算机计算25小时, 直到更快的计算机出现, 才使得2小时之内可以算出24小时的天气预报, 使得天气预报实用化. 这个, 也就是10倍的更新. 

快工具和慢工具的差别, 带来了一件事情可做与不可做的差别. 其实不光是表面上速度的改变, 对应内里是整个方法体系的本质改变才是关键. 比如, 在 UNIX 下数一个文档有几个a开头的词是很简单的事情, 只需要知道正则表达式和管道就行了. 在没有正则表达式和管道的环境里, 这个事情就比较难 (或者有更加好的方法我不知道?). 当然, 这事也可以做, 只是慢了10倍而已. 同理, 从纽约到华盛顿步行也能到, 就是慢了一点而已. 而汽车让从纽约到华盛顿变成了一件很平常的事情, 其实小汽车也就比步行快了不到20倍而已. 到图书馆查百科全书也是一种获取资料方式, 在网上 Google 也是一种方式, 后者(一分钟)比起前者(一小时), 也就快了10倍到100倍而已. 可不同的仅仅是速度么? 正则表达式提供了新的描述角度; 汽车是一种新的不耗费体力的快速交通工具; Google 是一种新的获取信息的手段, 这些速度的表面差异, 对应的内里, 是本质差异. 虽然改变的是速度, 却不仅仅是速度. 甚至, 我们可以大胆的断定, 如果没有本质的内里的变化, 速度也不可能有10倍的提升. 

我们都知道, 做事情要高效, 要 WikiWiki. WikiWiki,  是每一个想要管理好时间的人的圣杯, 是每一个想多做点事情的人的魔咒; 可是很显然, 平凡的工具, 至多越用越熟; 即使用到烂熟, 也不能带来本质的效率提升的; 一成不变的思想, 最多用到极致, 形成一个自我体系, 但跳出体系外, 是不能带来崭新的角度和本质的提升的. 特别的, 是在计算机科学以及计算机编程领域, 快工具和新思想层出不穷. 依我的观察, 在计算机科学的发展史中, 每一个时代都有很多新思想涌现, 带来的是革命性的思维方法和崭新的理论实践, 以及快好几个数量级的效率提升; 在在编程方面, 我们大多数人也见识了 UNIX 管道哲学, 和函数式编程的哲学对效率的提升.  这些新思想, 好工具, 是我们计算机科学领域最好的珠玑, 也是在大海边玩耍的孩子不可错过的晶亮的贝壳. 

那么, 心急的读者要问了, 到底哪些是晶亮的贝壳和藏着的珠玑呢? 别急, 我会把我见到的认为是晶亮的贝壳和珠玑的好东西记录在这里, 所以, 请继续关注我这个系列的后续文章 :) 

最后附送几个不算八卦的八卦, 算是本文花絮: 

1. 本文中间那句拗口的中文是翻译自 Software Tools 中的一句: < Many jobs will not get done at all unless they can be done quickly.> 里面还有 “We consider people cost a great deal more than machines, and the disparity will increase in the future” 以及 “The extra freedom permitted by got’s is just what you don’t want” 等道理深刻的话语.  有时候我真的怀疑, Software Tools 是一本讲哲学的书, 而不是一本编程书. 

2. Wiki 最早的思想来自于苹果机上的 hypertalk. 这个软件相当于是个人多媒体 Wikipedia. 苹果的 Applescript 自然语言编程的语法, 也是借鉴的这个软件的语言, 叫做 hypertalk. 这个软件称得上是个人计算机的 killer app, 但是不幸被苹果收购之后就中断开发了.  Steve Jobs 这家伙很没文化, 收购并扼杀了很多苹果上的经典软件, 这些故事等以后有空细说. 

3. 关于汽车之比人快10倍, 但是本质上改变了人的生活的例子是借用的 Knuth 的, 具体可见 <Mathematics and Computer Science: Coping with Finiteness>, 文章发在 1976 年的 Science. 这是一篇好文章!

4. Ward Cunningham 的网站, http://c2.com/cgi/wiki 是历史上最早的 wiki 百科, 只是全是关于计算机和编程的而已.

这七条都是我这个不怎么高效能编程的人悟到的. 不权威, 不一定全对. 

 

1. 使用工具帮你找 Bug, 而不是人工找. 

工具包括用单元测试, assert语句, 代码测试容器. 人工指用 print 和 debugger 一行一行跟踪. 我们知道, 编程中绝大部分时间是耗费在除 bug 上. 不同的人有不同的 debug 的方法. 我个人比较喜欢”极限编程(XP)” 学派的主义, 也就是说, 代码未动, 测试先行. 

单元测试中的红棒绿棒(熟悉 JUnit 的读者知道我在说什么)一出现, 哪里出了问题就一目了然. 单元测试的另外一个好处在于增加写程序的自信. 以前没用单元测试之前, 每天晚上改代码改到很晚的时候脑子常常不灵活, 把代码改错, 然后第二天来还要重头弄. 有了单元测试之后每天晚上保证测试全部过掉, 这样心理踏实, 睡觉也香, 早晨也不忙, 吃饭也棒. 

一般的语言都有 assert, 但是很少有人用. 其实 assert 是一个非常好的DEBUG 工具, C 的 assert 能够把哪一个文件哪一行出了错都告诉你. 不过我一般会自己写一个这样的 assert 宏:

#define ASSERT(value, msg) if (!(value)) {fprinft(stderr, "At file %s, line %d: \n message: %s\n", __FILE__, __LINE__, msg); exit(-1);}

这样的 ASSERT 可以带一个信息出来, 比起原来只告诉你哪个文件哪一行更加有价值. 

第三个是用容器帮你找 Bug. 这一点以 C/C++ 程序最为突出, 因为编译之后直接就是可执行代码, 运行时的信息不像 Java 和 Python 这样有 VM 的语言容易得到. 这时候, 我推荐 valgrind. 这个工具能够把 C/C++ 程序放到一个容器中执行, 记下每一个内存访问. 被这样的容器 debug 一下, 基本上指针指飞了 (Segmentation Fault) 的情况几乎就没有了. 想像一下是用 GDB 追踪非法指针和内存泄露方便, 还是用容器告诉你哪一个指针非法, 哪一个内存没释放方便 :)

 

2. 选用自动化工具构建

用 gcc 或者简单的 IDE 来编译和运行程序在编程初期是很快速的, 可是越到后来, 会越臃肿. 在编译的时候, 不同的参数, 不同的目标, 在 IDE/gcc 里面每次都要设定. 而且一般的 IDE 也不能做到自动解决依赖等高级方法. 因此, 最好的方法是用 Ant 或者 Makefile 管理项目. 这方面教程很多, 而且我估计编程的个个都知道. 不管项目大小, 注意频繁使用就是了. 

自动化测试也有很多工具, 特别是 GUI 和命令行测试的自动化, 工具链都很完整. 大公司里的程序员走这方面的流程都比较规范(我在西门子实习过), 但是小一点的公司中, 或者个人搞小项目的时候, 就不一定想得起来了(大部分我见到的程序员就手工来测试). 手工测试看上去快, 但是要是积累的次数多了就比较浪费时间了. 其实自动化测试工具的学习成本很低的, 事半功倍. 

 

3. 买本小书做参考, 而不是用 Google. 

这是大实话. 我大三开始学 Python 的时候, 语言特性并不熟悉, 手头也没有书, 因此常常连取个随机数都要上 Google 查一下库. 我发现, 不管网络多快, 自己搜索技术多牛, 还是没有手头一本书方便. 后来打印了一个7页的标准库的 cheatsheet, 编程立即行云流水. 我在实习的时候也观察到, 大部分时候程序员不可能记住一个框架所有的API, 所以他们要不等 IDE 几秒钟做代码补全, 要不一边翻文档一边做. 或许MSDN 这些本地文档系统比查书快吧, 但是用 Google 和网络搜索绝对比书慢. 现在因为工作原因, 常常要学一些新的语言, 我做的第一件事情, 就是把他的库接口的网页全部打印了下来. 

 

4. 用脚本语言开发原型

人月神话的作者 Brooks 说: 准备把第一版扔掉, 因为第一版必然要被扔掉. 这是大实话和真理. 既然第一版要被扔掉, 咱们就让第一版扔掉得越早越好. 说白了就是, 原型要快速的被开发. 

所谓的快速原型开发, 大致有两个捷径, 第一是只做核心的功能, 输入输出都是构造好的简单的例子. 第二是只做最简单的情况, 对于性能和健壮性什么的都不太考虑. 这两点, 恰好是脚本语言最擅长的. 脚本语言擅长于用精简的几行构造出复杂的功能, 并且语法很松散, 潜在假设程序是正确的. 

即使在代码编写阶段, 一些功能的实现, 也是要先写个简单的, 再慢慢打磨成复杂的. 脚本语言此时依然有用. 比如我在用 Java 的时候, 常常不确定一个函数返回的对象究竟某个属性是什么样的值. 这时候我就会用 Java 的 bsh 脚本写一行打印, 而不会写一个复杂的 out.println 再编译再运行再把那行删除掉. 当然, 这几年很流行动态语言, 原型和产品之间的差距已经变得很小了. 

 

5. 必要的时候, 程序要使用清晰的, 自我解释的文本文件作为日志输出. 

不知道各位调试程序的时候是不是和我一样, 看到不确定的和要跟踪的变量就直接插入一行 print. 我以前一直这样做, 但是频繁的插入这样的打印会使得屏幕的输出很乱, 不知道哪行是什么意思. 一个更加好的办法是写一个日志函数, 可以分也可以不分优先级, 总之保证 Debug 的时候的输出以一种统一的, 可管理的方式出现. 这样, 在最后发布稳定版本的时候, 只需要简单的几行命令就可以从代码中剔除所有的日志打印行. 

如果必然要输出日志, 最好要分配一个单独的命令行参数, 用来控制程序究竟输出不输出日志, 输出哪些日志. 一开始看上去这个是费时费力, 越到后来日志越多的时候, 就体会到方便之处: 有时候你只想要某一类日志, 可是其他的记录偏偏来捣乱. 多加一个参数可以使得程序更加灵活, 根本不需要去修改代码或者条件编译就能得到不同级别的程序日志.

日志和程序的输出结果一定要清晰且能自我解释, 否则不如没有日志. 我切身经历是这样的: 几个月前, 我一个程序跑了大约一天, 最后输出了很大的日志和结果. 但是很不幸的是, 结果里只有数字, 没有任何说明. 我自己都忘了每一行是什么意思. 而且更加麻烦的是程序的输出藏在重重判断和循环之内, 使得根本没有办法分析这一行输出对应的输入是什么. 于是, 最终只能再次浪费一天的时间让程序再跑一次.  经过这次教训, 我的程序日志和结果中插入了不少让人可读的内容. 这样, 即使程序丢失了, 结果还是能够被人解读的. 

更多的关于数据和程序结果要能自我解释的精彩论述, 可参见 More Programming Pearls 第四章. 

 

6. 使用命令行小工具操控分析你的结果和代码, 而不是用自己的眼睛和手.

我发现, 人有一个固有的习惯, 就是喜欢自己去”人工”, 而不喜欢用工具. 因为人工让人感觉工作更加刻苦, 更加快, 更加有控制感. 比如说吧, 上面我说的测试, 我就不只一次见到为了测一个交互式的命令行, 一个程序员宁愿老是每次打相同的三个命令, 而不愿意用一个简单的 expect. 再比如说, 面对长长的日志文件, 我见到很多人都是用文本编辑器直接打开, 用鼠标滚轮一行一行的往下翻, 而不是使用 grep. 包括看网页, 很多人从来不用查找功能, 而是一行一行的往下瞄. 包括打游戏也是, 好的UI脚本(不是外挂)一大把, 可是玩 WoW 的人很少用, 都喜欢自己重复点鼠标.

别看上面说的这些好像程序员没有, 其实我们常常陷入这个误区. 举个简单的例子, 一个 python 程序里面有十几个 print 函数, 我们想把这些打印全部灭掉, 一般人会打开文件慢慢瞄, 稍微高级一点的用查找, 找到了, 用快捷键删掉整行. 其实最好的方法根本都不要编辑器, 应该用 grep -v. 或者 sed, 但是这样的方法极少会有人用的. 我也是强迫自己无穷多次之后, 才渐渐的用这套快速的方法. 

 

7. 程序能跑就是万岁. 除非万不得已, 尽量不要在性能上优化你的代码

Knuth 名言: Premature optimization is the root of all evil. (提前优化是万恶之源). 一般我们写代码的时候, 不知不觉的就会觉得, 哎呀, 这样写效率不高, 我要构造一个数据结构啥啥. 随机访问一定要哈希表, 排序一定上快排, 查找一定要二分, 强连通分量一定要用 Tarjan 算法, 动规一定比穷举好等等, 这些竞赛的时候极限情况下正确的论断其实在实际环境中并不重要, 因为做编程的一开始关键是能跑, 而不是跑得快. 往往这么以优化, 程序很难 debug, 倒是还要去翻算法导论和TAoCP 看人家的二分怎么写的等等. 

在程序能跑的情况下, 优化也要特别小心. 我曾经有一个程序, 大约有 90% 的运算是查表, 只有 1% 的是乘法, 另外是一些判断和把插到的结果插入到一个集合中. 我的查表是用的最土的 list.index. 按照正常的想法, 应该把这个优化成哈希表. 而实际上我用 profile 工具一看, 才知道, 原来是插入到一个集合的操作费时间, 因为每次都需要 extend, 涉及到很多内存分配的操作. 我做过非常多的 profile 测试, 没有一次不出乎我预料的. 程序运行时间总是在自己不认为浪费的地方被浪费掉. 因此, 就算万不得已优化, 也务必要先做一下 profiling. 我喜欢 python 的地方就在于, 他的 profiling 只需要一行语句就完成了, 而且结果具体干净. 其他的语言, 至今没见到这么简单的 profiling 工具. 

 

另外: 用两个或者大于两个显示器. 不要用或者少用鼠标.

我用苹果 Mac OS X 两年了, 越用越觉得, 其他操作系统要从苹果的 Mac OS X 上学习很多东西. 特别是图形界面的统一的脚本化接口, 我认为这对未来的操作系统是一个很好的启示, 也是完全用命令行工作哲学在图形界面时代的一个不可缺少的部分.

起因是最近我在写一系列的中文文章, 因为几乎没有数学公式, 而我又喜欢纯文本的工作方式, 因此在排版上我选择了reStructuredText. 一般来说, 英文文本我都是直接在 vim 下工作. 可是苹果下的 vim 对中文的支持不是想像的那么好, 至少中文输入法打出来的常常有乱码. 名动天下的 TextMate 固然好, 显示中文却只有半个字那么宽, 看上去很不舒服. 其他的编辑器要么收费, 要么嫌大, 要么不顺手, 因此落到最后还是用了苹果的 TextEdit.

但是写技术文章的人都知道, 写文章不仅是打字这么简单. 特别是技术文章, 常常需要离开编辑环境跑脚本, 查Google, 看 wiki. (我不知道别人是不是这样, 至少我为了写出质量较好的文章, 写的时候也要做很多功课). 特别是因为我在用 reStructuredText, 一个最基本的需求就是处理当前文本生成 HTML/LaTeX 然后预览. 这些需求, 在 VIM 和 TextMate 中都是原生支持的, 很简单能做到. 但是在 TextEdit 这样的软件中, 乍一看, 毫无办法. 所幸的是, UNIX 家族的操作系统, 特别是苹果, 秉承了 简单的事情要有简单的解法, 复杂的事情要有可能的解法 这样的设计思路, 提供了不止一条解决问题的方法. 因为苹果设计了一个叫做”脚本化应用程序”的东西, 使得这些任务可以很简单的完成. 我研究了几天, 学到了一些东西, 拿出来和大家分享.

1. 最常常被忽略的菜单栏, Service 菜单.

用具体的例子来说可能更加简单. 用 Gmail 的各位都知道, 如果邮件里面有 90081243455 这样的数字串, 或者有 北京是西城区XX路YY号 这样的地址, Gmail 就很贴心的显示出 “使用 Google 追踪快递包裹” 或者 “使用地图查看地址” 这样的链接. 也就是说, Gmail 会识别出这些特定的样式, 然后给你一个上下文相关的(也就是说, 和你内容相关的) 服务. 这样的服务贴心舒适, 相信不少人都很喜欢. 一般我们把这个叫做上下文相关的服务.

再说 Firefox. 前不久 Firefox 出了一个很贴心的小插件, 叫做 Ubiquity, 我在 Firefox 下几乎离不开它: 选中任何一行地址, 只需要敲几个键, 就可以查 Google Map. 选中任何一个名词, 可以立即查 Wikipedia, 等等. 一般我们把也把这个叫做基于选定的服务.

苹果上的 Service 菜单, 就是这样的一个基于上下文相关和基于选定的服务. 举个最简单的例子, 如果你的机器上装有 Skype, 然后你选中了一串电话号码, 这时候你可以到 Service 菜单中选择 Skype -> Call this number. Skype 就会自动拨打这个电话. 或者选中一个单词, Service 菜单中就会自动出现: Look up in the Dictionary. 选中它以后, 你机器上的字典就会跳出来, 把单词的释义呈现给你. 所有的这一切, 都是基于苹果的一个叫做 System Service 的框架. 简单的说, 每个应用程序告诉系统: 在什么情况下, 你给我什么信息, 就可以让我做什么事情. 这样, 在选定的上下文符合条件的时候, 你就可以在菜单中选择这个服务. 这个东西方便无比. 比如说, 常常老板在邮件里面写: 你这周做啥啥. 我就可以选中整行句子, 按下 ⇧⌘ Y. 这样整行句子就被纪录到 Stickies 里面去了, 相当于随时写下一个便签.

现有的菜单栏固然好, 但是自己编写的更加好玩. 所幸的是, 你不需要会苹果的生涩的 Obj-C 才能编写菜单. 有一个强大的工具, 叫做 ThisService, 能够把 Ruby, Python, Applescript 和 shell 脚本都包装成 Services. 比如说我昨天就花了一分钟写了一个很简单的叫做 Translate 的脚本, 通过 Python 提交文本给 Google Translate, 返回中文. 在阅读有很多不认识的单词的新闻文章时候, 只要选中段落, 选择这个 Translate. 一个翻译成中文的窗口就出现了. 在这个软件的帮助下, 任何脚本都能点一下鼠标就变成一个服务: 具体我就不一一详述了.

总的来说, 我们可以注意到, 在日常使用计算机的时候, 一个应用程序中的一些内容(文本或者图像), 往往要作为另外一个程序的输入, 交给另外一个程序处理. 比如选中的单词查字典, 选中的地址查阅地图, 和选中的电话号码拨打, 选中的句子翻译等等, 这些需求都是存在的. Windows 家族解决问题的方法, 是通过程序切换和剪切板. 而苹果就是通过统一的接口来完成的, 思想和UNIX管道是非常相似一脉相承的 (当然, windows 家族从来就没有 UNIX 家族管道这个思想, 所以也就想不出统一接口这样的设计了).

在苹果下, 统一脚本接口可以用来处理像 Service 这样的程序之间通信的问题, 这个是 windows 家族从来没有想过的. 同时, 据我个人经验, 这个思想在 Linux/X11 家族也没有全部实现. 在 Linux 命令行下, 我们都知道, 不同的小工具之间是正交的, 一个程序的输出是一个程序的输入, 这个 UNIX 哲学长盛不衰. 直到今天, 正常的 Linux/UNIX 用户也会天天用管道做程序之间的通信. 可是在图形界面出现后, 这样的好传统在 X11 系统中似乎没有被继承. 比如, 不能选中一段文字直接送给 wc 计算字数. 究其原因, 是因为图形界面再也没有明显的输入输出这个概念了. 字符串和图像变成了要在程序之间传送的基本单位. 在这种情况下, 就需要不仅仅以文件为基本单位, 而是以被选中的那些字符串, 句子, 链接, 图片等为基本单位的通信. 在这个层面上, 苹果的确先行一步. (当然 Emacs VIM 等等也是可以通信和调用外部程序的, 但是毕竟不是整个系统都能这样).

2. 史上最简单的语言, AppleScript.

故老相传, 当年 Knuth 大神访问苹果, 总裁 乔布斯 同学发挥爱吹牛的光荣传统, 说: 高教授, 您来了. 我特别崇拜你, 您的书我都读过. 各位读者都知道, 要是Jobs 真的把高教授的书都读过, 全世界大学计算机系99%的教授基本上都要自杀让位给Jobs. 所以, 高爷子知道他吹牛皮. 以下的故事分两个版本, 第一个版本是硅谷的版本, 是高爷子当即就说: 斯蒂夫同学, 你又扯淡忽悠了. 第二个版本是我的版本: 说高爷子一听, 也不生气, 心想, 你我共同作为硅谷两个最懂字体设计的搞IT的, 我也不好得罪你, 且问你一道简单的问题, 看你读了我哪几本书. 于是高爷子眼珠一转, 题上心来, 问到: 乔布斯同学, 请问世界上最简单的编程语言是啥子呢? 乔布斯是个不懂技术的主. 想起当年和 Woz 合作的时候搞的东西叫做 BASIC, 听名字就觉得简单, 于是说道: 乃是 BASIC. 高爷爷说, 还有比这个更简单的么. (话说高爷爷心想, 我手里握着 TeX, Metafont, MIX, Literate Programming, CWEB 等几张王牌, 我就不信你说不出一个搭不上边的). 乔布斯同学哪能体会到高爷爷的良苦用心, 他又不会编程, 于是就杜撰到: 我们公司最近正在开发世界上最简单的一个语言: 叫做苹果脚本(AppleScript). 高爷爷一听就知道又是吹牛, 于是立即回复: 斯蒂夫, 你又扯淡忽悠了. 斯蒂夫同学哪服软, 于是要求开发部门开发一个连他都懂怎么用的语言. 于是 AppleScript 就被捣鼓出来了.
(以上故事纯属扯淡忽悠)

话说AppleScript 有多简单呢? 就和说话一样简单. 我曾经也写过介绍. 再这里再给一个例子:


tell application "TextEdit"
set filename to name of document 1
print filename
end tell

看, 明明就是英语嘛! 这么平铺直叙几乎没有语法的编程语言, 真的是世界上最简单的编程语言唉.

别看这个语言简单, 威力可不小. Mac 上几乎所有的应用程序, 都能够用这个语言去控制, 特别是 iTunes 甚至还有一个专门的网站, 专门就让你下载千奇百怪的控制脚本.

Applescript 还有两个好处: 1. 他是苹果原生支持的, 可以直接编译成苹果的应用程序来用. 因此, 简单的任务处理都可以用这个语言. 比如我们上面说的做成 Service 的, 也可以用 Applescript 来实现. 2. 他能够和其他应用程序交互. 其实在苹果系统中, 在脚本桥技术(Script Bridging) 出现之后, 理论上C/Javascript/Python/Ruby 都是可以和应用程序交互的. 可是AppleScript 的支持更加好, 更加直接. 比如取当前播放的iTunes乐曲名字, 可以用
AppleScript:

tell application "iTunes" to get the name of the current track

也可以用 Python

from Foundation import *
from ScriptingBridge import *
iTunes = SBApplication.applicationWithBundleIdentifier_("com.apple.iTunes")
print iTunes.currentTrack().name()

显然 Python 要显得笨重一些.

同时, AppleScript 也是苹果中小机器人 Otto 的底层技术, 因此在整个系统中发挥了基础性的作用. 虽然学习 AppleScript 显得很偏很不入流, 但是在苹果上做一些小任务有时候还是用得着的. 比如说我的让 TextEdit 支持 ⇧⌘ R 自动编译的例子, 就是先写了一串脚本, 取当前文件名, 然后使用 do shell script 这个指令调用 make. 这样, 调用这个脚本, 就可以完成自动编译. 用快捷键来调用脚本, 有两种方式, 一种是通过把脚本拷贝到应用程序的脚本目录并且添加一个菜单项. 另一种是用 QuickSilver 来帮忙了. 第一种方法应该最简单, 但是我没有尝试成功, 有兴趣的读者可以读这篇文章.

3. QuickSilver

我曾经间或的提到过 QuickSilver. 刚开始我并不能体会到他的魔力, 直到最近在玩脚本控制的时候才发现. 简单的说, QuickSilver 就是能让你用键盘控制程序的程序. 或者说, QuickSilver 就是键盘命令中枢. 比如说播放音乐, 只要一键激活 QuickSilver, 再打一下 play (有时都不要打全), 回车. iTunes 就放起音乐了. 上面我们说的任何控制系统的脚本, 都能够被 QuickSilver 控制. 我在拔掉你的鼠标一文中也提到, “你要是苹果用户又不用QuickSilver或命令行, 那你是把苹果当Windows用”.

QuickSilver 作为应用程序快速启动的功能大家想必都很熟悉. 控制 iTunes 大家也得心应手. 我就介绍一个被大家忽略的功能: 设置上下文相关的快捷键. 传统上, 快捷键是被应用程序定死的. 即使你写了一个扩展应用程序的脚本, 你也得点点鼠标去调用, 而不能设置键盘快捷键去调用. 解决的方法有三个, 第一个是我在第一部分讲的, 包装成一个 Service. 这样的好处是可以包装出一个快捷键, 坏处是这个Service 是全局都有用的, 不管你在哪个应用程序里面, 这个 service 都能用. 第二种是我在第二部分提到的我没有尝试成功的, 即添加一个菜单项目的方法. 第三个就是采用 QuickSilver 的 Trigger 功能了. QS 用户可以使用 ⌘’ 调出 trigger, 然后添加一个 Custom Trigger, 把目标指向自己写好的一个 AppleScript, 给这个 trigger 分配一个快捷键, 并且把使用的范围 scope 限定在一些应用程序中. 这样, 不费任何功夫, 只写一个简单的脚本, 您就可以在你的计算机上把你的 TextEdit 也改造成一个功能强大的编辑器了.

结语: 如我在拔掉你的鼠标一文中说的, 完全使用键盘能强迫人使用高效简洁和正确的工具. 随着图形界面和多媒体的出现, 鼠标的反而成了一个最常见的动作. 其实认真分析鼠标点选操作就发现, 无非是两种: 一种是用鼠标选择内容, 准备用一定的工具和程序处理这个内容, 还有一种是鼠标选择操作. 前者对内容的选择, 鼠标是高效的. 而后者对操作的选择, 鼠标是低效的 (因为键盘快捷键和命令行更加高效). 因此, 如果我们想要高效的工作, 就要避免后一种操作, 并且进一步解放前者.

对后一种的避免很简单: 拔掉鼠标, 强迫用键盘. 对前者的解放, 随着各种各样如 Service, Ubiquity 以及上下文敏感程序的兴起, 使得我们不需要在不同的程序之间拷贝来拷贝去, 而是直接通过程序之间的标准接口通信, 使得我们甚至不需要切换程序. 所有的都应该这些基于命令行和后台来完成, 而不是通过分散注意力切换程序来完成. 这些新的工具, 更加符合一个高效能的现代人的需求, 更加能够提升你的效率. 当然, 决定做事的效率的不仅仅是工具, 因此, 不要迷恋这些工具. 为了获得 1% 效率的提升, 而过于强调工具的各种淫巧, 反而降低效率. (警告: 这些工具都是非常迷人的, 很容易一用就爱上它们. 记住, 效率是最好的判断标准 :).

附 “完全用命令行工作系列” 文章一共五篇, 写了也不少了, 有些工具我一笔带过, 有些我花了几句话讲了一下. 基本上我介绍到的提到的, 都是我自己天天用的, 也是我认为非常有用的. 我写作的时候也都是带着 “Smart and Get Things Done” 的哲学, 只介绍干净的设计良好的软件. 各位想实践 GTD 的读者可以自行选择趁手工具. 有更多的 GTD 的完全用 键盘/命令行 工作的软件, 也不妨向我推荐.

完全用命令行工作-3: 常用工具

完全用命令行工作-2: 常用软件

完全用命令行工作-1: 拔掉你的鼠标

完全用命令行工作: 几条趁手语句

(这篇文章是给想学习 Linux/UNIX 但是不知道前方有什么东西要学以及用了Linux 一段时间后想要快速提高生产率的人看的)

Linux 本身只是一个内核, 所谓的内核, 就是你看不见摸不着的东西. 平时打交道的, 都是应用软件和一些常用的命令. 很多人说学习Linux, 实际上学习的是这些常用命令和软件的用法. 这些知识, 其实不光是Linux 上通用, 在苹果, Solaris, BSD 等系统上都是通用的.

同时, 只有学会了这些应用软件, 才能随心所欲的驾驭计算机. 否则, Linux 系统的魔力就体现不出来. 有一种观点说: 反正是完成工作, Windows 高效就用 Windows. 而实际上, 如果对 Linux 上常用应用软件稍做一些探索, 就可以发现, 大部分日常工作都是用 Linux 系统上的软件高效, 因为他们从一开始就比 Windows 设计优良. (当然, 我依旧承认在多媒体方面 Windows 上的软件如 Photoshop 等专业软件的确比 Linux 上的对应软件要强大. 但是这个强大是 Adobe 的软件, 不是微软的操作系统 ).

下文就是介绍 Linux 上高效工作的一些常用软件.

GNU coreutils: 很多人学 Linux, 其实就是学这里面的命令. 比如 cat, ls, head, kill,  这些都是一个基本系统必备的工具集合. 学会了这个里面的命令, 基本上任何 *nix 系统能搞掂. 比如你以后用苹果Mac 也好, unix 服务器也罢, SUN 的Solaris 也行, 都是一样的用法. 而且这里面的工具的确很强大, 比如以前我说的求集合的交集和差集, 就是这里面的小工具拼一下就出来了.

GNU findutils: 这个工具包能够帮助你在 Linux 系统上找文件, 或者对满足一定条件的文件做一定的操作. 其中的 find 和 xargs, 可以变化出无数组合, 让对文件的批量处理变得异常简单. 比如说, 把当前目录及其子目录下面所有的早于2002年的照片文件都删除这样一个操作, 在 Linux 下面就是一行命令, 而在 Windows 底下就要求助于专门的照片管理软件了.

cron: 假如你想每天定时让你的计算机做一件事情(比如, 控制电饭锅做饭), 或者每个周日的晚上帮你把文件清理清理, 或者每天早晨给你抓取各大网站的新闻存成摘要投递到你的桌面上, 你肯定需要 cron 这样一个强大的东西. 他的使用出乎想象的简单, 就在一个指定的文件里面写上什么时间做什么就行了, 比设置 Made In China 的闹钟还简单. 这个定时做事情带来的效率提醒是意想不到的.

screen: 如果你登录到一个机器工作, 退出的时候还想保持命令行继续存在, 下次登录还能继续工作, 或者你想和另一个人同时在一个命令行界面里操作, 或者你想只登录一次, 却拥有多个可用的终端窗口用来工作, 那么恭喜你, screen 是你想要的. screen 可以帮助你模拟出多个输入, 并且方面的切换, 还能把终端挂起来, 下次连上去继续工作, 这个对于在远程服务器上工作的人是杀手工具.

expect: 如果你非常讨厌每次登录telnet 的时候都要输入一次用户名密码, 如果你每次用某些软件的时候都要千篇一律的输入很多东西; 如果你想挂机刷机赚经验, 如果你想测试你的程序是不是正常工作, 那么, expect 是一定要使用的. expect 本来是用来做程序测试的, 看程序是不是有预想的输出. 有创意的玩家把他用去自动上论坛挂机或者自动登录FTP了. 如果想要自动化一些在命令行下交互性质的事情, expect 是很好的选择. 而且, 学习一下, 估计也就10分钟.

wget/curl: 支持多线程通配符递归, 断点续传等一切迅雷有的功能的下载器, 而且没有烦人的小广告. 如果要常常下载东西, 这个比迅雷好玩多了.

make: 以上说的都是单个工具. 而这个make 就是把工具组合起来的. 比如说, 写一个报告可能涉及画插图, 写文字, 生成报表, 做排版. 这些任务之间有依赖关系, 比如报表依赖于数据, 插图依赖于一个脚本, 排版依赖于一个格式规定. 数据更新了, 报表就要更新. 所有的这些任务和任务的依赖关系, 都可以写一个简单的makefile, 这样 make 就可以托管了. 假如数据更新了, 生成报表的时候只要重做报表, 插图就不需要重做. make 能自动分析这些依赖关系, 使得只执行必要的任务, 极大的减少了人工管理的负担. 事实上, 我的论文和家庭作业报告基本都是用 make 管理的.

convert/display: 这是使用 Linux 做日常工作的人必然要碰到的两个程序. 简单的说, 这是两个无所不能的程序. 其中 convert 能把一切能显示的图像格式转化成另一个格式. 比如png 和 pdf 之间的转化, 就是一句话. 而display 则是显示一切能现实的. 这两个, 还有其他的一些程序,  都属于ImageMagik 这个包. 用多了, 就会感叹, 其实把图片加加对比改改大小或者旋转旋转这些事情, 就是一行命令, 完全不需要开一个窗口点鼠标. ImageMagik 的设计也充分体现了 Facet 的设计模式, 接口非常简单, 自然, 常常和图片打交道的人熟悉使用以后, 效率能成倍提高.

另外, 学会正则表达式和一种脚本语言是必须的步骤. 其实这些都不是想象的那么难, 只不过是换一种眼光看问题罢了.

总的来说, 就一点: 在使用计算机做一件事情前, 先想想, 我是不是聪明到是世界上第一个遇到这个问题的人. 如果不是, 就动脑筋想想这个事情是不是可以分成几件小的步骤, 这些小的步骤是不是有现成的工具了. 一般情况下 99% 的都有前人的软件. 如果实在没有, 自己就开始写一个. 通常来说, Linux 比 Windows 效率高的所在, 在于工具的高效配合, 而不是直接找到一个包打四方的巨无霸的工具.

(本文覆盖肯定不全, 欢迎留言补充)