皇上,还记得我吗?我就是1999年那个Linux伊甸园啊-----24小时滚动更新开源资讯,全年无休!

为什么优秀的程序员喜欢命令行?

优秀的程序员

要给 优秀的程序员 下一个明确的定义无疑是一件非常困难的事情。擅长抽象思维、动手能力强、追求效率、喜欢自动化、愿意持续学习、对代码质量有很高的追求等等,这些维度都有其合理性,不过又都略显抽象和主观。

(图片来自:http://t.cn/R6I1yhJ)

我对于一个程序员是否优秀,也有自己的标准,那就是 TA 对 命令行的熟悉/喜爱程度 。这个特点可以很好的看出 TA 是否是一个 优秀的(或者潜在优秀的)程序员。我周围就有很多非常牛的程序员,无一例外都非常擅长在命令行中工作。那什么叫熟悉命令行呢?简单来说,就是 90%的日常工作内容可以在命令行完成。

当然,喜欢/习惯使用命令行可能只是表象,其背后包含的实质才是优秀的程序员之所以 优秀 的原因。

自动化

Perl 语言的发明者 Larry Wall 有一句名言:

The three chief virtues of a programmer are: Laziness, Impatience and Hubris. – Larry Wall

懒惰(Laziness)这个特点位于 程序员的三大美德 之首:唯有懒惰才会驱动程序员尽可能的将日常工作自动化起来,解放自己的双手,节省自己的时间。相比较而言,不得不说, GUI 应用天然就是为了让 自动化 变得困难的一种设计(此处并非贬义,GUI 有着自己完全不同的目标群体)。

(图片来自:http://t.cn/R6IBgYV)

GUI 更强调的是与人类的直接交互:通过视觉手段将信息以多层次的方式呈现,使用视觉元素进行指引,最后系统在后台进行实际的处理,并将最终结果以视觉手段展现出来。

这种更强调 交互 过程的设计初衷使得 自动化 变得非常困难。另一方面,由于 GUI 是为交互而设计的,它的响应就不能太快,至少要留给操作者反应时间(甚至有些用户操作需要人为的加入一些延迟,以提升用户体验)。

程序员的日常工作

程序员除了写代码之外,还有很多事情要做,比如自动化测试、基础设施的配置和管理、持续集成/持续发布环境,甚至有些团队还需要做一些与运维相关的事情(线上问题监控,环境监控等)。

  • 开发/测试
  • 基础设施管理
  • 持续集成/持续发布
  • 运维(监控)工作
  • 娱乐

而这一系列的工作背后,都隐含了一个 自动化 的需求。在做上述工作时,优秀的程序员会努力将其自动化,如果有工具就使用工具;如果没有,就开发一个新的工具。这种努力让一切都尽可能自动化起来的哲学起源于 UNIX 世界。

而 UNIX 哲学的实际体现则是通过 命令行 来完成的。

Where there is a shell, there is a way.

UNIX 编程哲学

关于 UNIX 哲学,其实坊间有多个版本,这里有一个比较 详细的清单 。虽然有不同的版本,但是有很多一致的地方:

  1. 小即是美
  2. 让程序只做好一件事
  3. 尽可能早地创建原型 (然后逐步演进)
  4. 数据应该保存为文本文件
  5. 避免使用可定制性低下的用户界面

审视这些条目,我们会发现它们事实上促成了自动化一切的可能性。这里列举一些小的例子,我们来看看 命令行 工具是如何通过应用这些哲学来简化工作、提高效率的。一旦你熟练掌握这些技能,就再也无法离开它,也再也忍受不了低效而复杂的各种 GUI 工具了。

命令行如何提升效率

一个高阶计算器

在我的编程生涯早期,读过的最为振奋的一本书是 《UNIX 编程环境》,和其他基本 UNIX 世界的大部头比起来,这本书其实还是比较小众的。我读大二的时候这本书已经出版了差不多 22 年 (中文版也已经有 7 年了),有一些内容已经过时了,比如没有返回值的 main 函数、外置的参数列表等等,不过在学习到 HOC(High Order Calculator) 的全部开发过程时,我依然被深深的震撼到了。

简而言之,这个 HOC 语言的开发过程需要这样几个组件:

  • 词法分析器 lex
  • 语法分析器 yacc
  • 标准数学库 stdlib

另外还有一些自定义的函数等,最后通过 make 连接在一起。我跟着书上的讲解,对着书把所有代码都敲了一遍。所有的操作都是在一台很老的 IBM 的 ThinkPad T20 上完成的,而且全部都在命令行中进行(当然,还在命令行里听着歌)。

这也是我第一次彻底被 UNIX 的哲学所折服的体验:

  • 每个工具只做且做好一件事
  • 工具可以协作起来
  • 一切面向文本

下面是书中的 Makefile 脚本,通过简单的配置,就将一些 各司其职 的小工具协作起来,完成一个编程语言程序的预编译、编译、链接、二进制生成的动作。

虽然现在来看,这本书的很多内容已经过期(特别是离它第一次出版已经过去了近 30 年),有兴趣的朋友可以读一读。这里有一个 Lex/Yacc 的小例子 的小例子,有兴趣的朋友可以看看。

当然,如果你使用现在最先进的 IDE(典型的 GUI 工具),其背后做的事情也是同样的原理:生成一个 Makefile,然后在幕后调用它。

基础设施自动化

开发过程中,工程师还需要关注的一个问题是:软件运行的环境。我在学生时代刚开始学习 Linux 的时候,会在 Windows 机器上装一个虚拟机软件 VMWare,然后在 VMWare 中安装一个 Redhat Linux 9

(图片来自:http://t.cn/R6IBSAu)

这样当我不小心把 Linux 玩坏了之后,只需要重装一下就行了,不影响我的其他数据(比如课程作业、文档之类)。不过每次重装也挺麻烦,需要找到 iso 镜像文件,再挂载到本地的虚拟光驱上,然后再用 VMWare 来安装。

而且这些动作都是在 GUI 里完成的,每次都要做很多重复的事情:找镜像文件,使用虚拟光驱软件挂载,启动 VMWare,安装 Linux,配置个人偏好,配置用户名/密码等等。熟练之后,我可以在 30 - 60 分钟内安装和配置好一个新的环境。

Vagrant

后来我就发现了 Vagrant,它支持开发者通过配置的方式将机器描述出来,然后通过命令行的方式来安装并启动,比如下面这个配置:

它定义了一个虚拟机,使用 Ubuntu Precise 64 的镜像,然后为其配置一个网络地址 192.168.2.100,定义好之后,我只需要执行:

我的机器就可以在几分钟内装好,因为这个动作是命令行里完成的,我可以在持续集成环境里做同样的事情 – 只需要一条命令。定义好的这个文件可以在团队内共享,可以放入版本管理,团队里的任何一个成员都可以在几分钟内得到一个和我一样的环境。

Ansible

一般,对于一个软件项目而言,一个全新的操作系统基本上没有任何用处。为了让应用跑起来,我们还需要很多东西。比如 Web 服务器、Java 环境、cgi 路径等,除了安装一些软件之外,还有大量的配置工作要做,比如 apache httpd 服务器的文档根路径,JAVA_HOME 环境变量等等。

(图片来自:http://t.cn/R6IBZKm)

这些工作做好了,一个环境才算就绪。我记得在上一个项目上,不小心把测试环境的 Tomcat 目录给删除了,结果害的另外一位同事花了三四个小时才把环境恢复回来(包括重新安装 Tomcat,配置一些 JAVA_OPTS,应用的部署等)。

不过好在我们有很多工具可以帮助开发者完成环境的自动化准备,比如:ChefPuppetAnsible。只需要一些简单的配置,然后结合一个命令行应用,整个过程就可以自动化起来了:

上边的配置描述了安装 graphite-carbon、配置 apahce 等很多手工的劳动,开发者现在只需要执行:

就可以将整个过程自动化起来。现在如果我不小心把 Tomcat 删了,只需要几分钟就可以重新配置一个全新的,当然整个过程还是自动的。这在 GUI 下完全无法想象,特别是在有如此多的定制内容的场景下。

持续集成/持续发布

日常开发任务中,除了实际的编码和环境配置之外,另一大部分内容就是持续集成/持续发布了。借助于命令行,这个动作也可以非常高效和自动化。

Jenkins

持续集成/持续发布已经是很多企业 IT 的基本配置了。各个团队通过持续集成环境来编译代码、静态检查、执行单元测试、端到端测试、生成报告、打包、部署到测试环境等等。

比如在 Jenkins 环境中,在最前的版本中,要配置一个构建任务需要很多的 GUI 操作,不过在新版本中,大部分操作都已经可以写成脚本。

这样的方式,使得自动化变成了可能,要复制一个已有的 pipline,或者要修改一些配置、命令、变量等等,再也不需要用鼠标点来点去了。而且这些代码可以纳入项目代码库中,和其他代码一起被管理、维护,变更历史也更容易追踪和回滚(在 GUI 上,特别是基于 Web 的,回滚操作基本上属于不可能)。

上面这段 groovy 脚本定义了三个阶段,每个阶段中分别有自己的命令,这种以代码来控制的方式显然比 GUI 编辑的方式更加高效,自动化也变成了可能。

运维工作

自动化监控

Graphite 是一个功能强大的监控工具,不过其背后的理念倒是很简单:

  • 存储基于时间线的数据
  • 将数据渲染成图,并定期刷新

用户只需要将数据按照一定格式定期发送给 Graphite,剩下的事情就交给 Graphite 了,比如它可以消费这样的数据:

第一个字段表示数据的 名称 ,比如此处 instance.prod.cpu.load 表示 prod 实例的 CPU 负载,第二个字段表示数据的 ,最后一个字段表示时间戳。

这样,Graphite 就会将所有同一名称下的值按照时间顺序画成图。

(图片来自:http://t.cn/R6IxKYL)

默认地,Graphite 会监听一个网络端口,用户通过网络将信息发送给这个端口,然后 Graphite 会将信息持久化起来,然后定期刷新。简而言之,只需要一条命令就可以做到发送数据:

date +%s 会生成当前时间戳,然后通过 echo 命令将其拼成一个完整的字符串,比如:

然后通过管道 | 将这个字符串通过网络发送给 graphite.server 这台机器的 2003 端口。这样数据就被记录在 graphite.server 上了。

定时任务

如果我们要自动的将数据每隔几秒就发送给 graphite.server,只需要改造一下这行命令:

  1. 获取当前 CPU 的 load
  2. 获取当前时间戳
  3. 拼成一个字符串
  4. 发送给 graphite.server2003 端口
  5. 每隔 5 分钟做重复一下 1-4

获取 CPU 的 load 在大多数系统中都很容易:

这里的参数:

  • -A 表示统计所有当前进程
  • -o %cpu 表示仅显示 %cpu 列的数值

这样可以得到每个进程占用 CPU 负载的数字:

下一步是将这些数字加起来。通过 awk 命令,可以很容易做到这一点:

比如要计算 1 2 3 的和:

通过管道可以讲两者连起来:

我们测试一下效果:

看来还不错,有个这个脚本,通过 crontab 来定期调用即可:

当然,如果使用 Grafana 等强调 UI 的工具,可以很容易的做的更加酷炫:

(图片来自:http://t.cn/R6IxsFu)

想想用 GUI 应用如何做到这些工作。

娱乐

命令行的 MP3 播放器

最早的时候,有一个叫做 mpg123 的命令行工具,用来播放 MP3 文件。不过这个工具是商用的,于是就有人写了一个工具,叫 mpg321,基本上是 mpg123 的开源克隆。不过后来 mpg123 自己也开源了,这是 后话不提

将我的所有 mp3 文件的路径保存成一个文件,相当于我的歌单:

然后我将这个歌单交给 mpg321 去在后台播放:

这样我就可以一边写代码一边听音乐,如果听烦了,只需要将这个后台任务切换到前台 fg,然后就可以关掉了:

小结

综上,优秀的程序员借助命令行的特性,可以成倍(有时候是跨越数量级的)提高工作效率,从而有更多的时间进行思考、学习新的技能,或者开发新的工具帮助某项工作的自动化。这也是 优秀的程序员之所以优秀 的原因。而面向手工的、原始的图形界面会拖慢这个过程,很多原本可以自动化起来的工作被淹没在“简单的 GUI”之中。

(图片来自:http://cargocollective.com/)

最后补充一点,本文的关键在于强调 优秀的程序员 与命令行的关系,而不在 GUI 程序和命令行的优劣对比。GUI 程序当然有其使用场景,比如做 3D 建模、GIS 系统、设计师的创作、图文并茂的字处理软件、电影播放器、网页浏览器等等。

应该说, 命令行优秀的程序员 之间更多是关联关系,而不是因果关系。在程序员日常的工作中,涉及到的更多的是一些需要命令行工具来做支持的场景。如果走极端,在不适合的场景中强行使用命令行,而置效率于不顾,则未免有点矫枉过正,南辕北辙了。

分享到:更多 ()