很多人都会用一些“脚本语言”(scripting language),却很少有人真正的知道到底什么是脚本语言。很多人用 shell
写一些“脚本”来完成日常的任务,用 Perl 或者 sed
来处理一些文本文件,很多公司用“脚本”来跑它们的“build”。那么,到底什么是“脚本语言”与“非脚本语言”的区别呢?
首先我们来看看“脚本”这个概念是如何产生的。使用 Unix
系统的人都会敲入一些命令,而命令貌似都是“一次性”或者“可抛弃”的。然而不久,人们就发现自己在重复的敲入类似的命令,所以有人就发明了“脚本”这东西。它的设计初衷是“批量式”的执行命令,你在一个文件里把命令都写进去,然后执行这个文件。可是不久人们就发现,这些命令行其实可以用更加聪明的方法构造,比如定义一些变量,或者根据系统类型的不同执行不同的命令。于是,人们为这脚本语言加入了变量,条件语句,数组,等等构造。“脚本语言”就这样产生了。
然而人们却没有发现,其实他们根本就不需要脚本语言。因为脚本语言里面的这些结构,在任何一种“严肃”的程序语言(比如
Java,Scheme)里面,早就已经存在了。脚本语言的“优势”,其实只在于它不需要事先“编译”,它“调用程序”的时候,貌似可以少打几个字。然而,这个优势却是非常不明显的。你完全可以想一个自动的办法,先调用编译器,生成机器代码,然后执行它。或者你可以选择一种有解释执行方式的“严肃语言”(比如
Scheme)。实际上,很多 Scheme
解释器都会进行一定程度的“编译”,有些编译为字节码,有些编译为机器代码,然后再执行。所以在这种情况下,“编译性语言”与“解释性语言”,几乎没有本质上的区别。
跟 Java 或者 Scheme
这样的语言截然不同,“脚本语言”往往意味着异常拙劣的设计,因为它的设计初衷往往是目光短浅的。这些语言里面往往充满了历史遗留下来的各种临时的
hack,几乎没有“原则”可言。然而,在当今现实的工程项目中,这种脚本却占据了它们不该占有的地位。例如很多公司使用 shell
脚本来处理整个软件的“build”过程或者测试过程,其实是相当错误的决定。因为一旦这种 shell
脚本日益扩展,就变得非常难以控制,经常出现一些莫名其妙的问题,却找不到到底哪里出了问题。
我发现,“脚本语言”这个概念貌似在很多人的心目中根深蒂固了,仿佛他们头脑里的程序设计原则,一遇到“写脚本”这样的任务就完全崩溃了似的。很多平时写非常聪明的程序的人,到了需要处理“系统管理”任务的时候,就开始写一些
shell 脚本,或者 Perl
脚本。他们写这些脚本的时候,往往完全的忘记了程序设计的基本原则,例如“模块化”,“抽象”等等。他们大量的使用“环境变量”一类的东西,仿佛处理系统管理就应该这样做似的。
到后来,他们开始耗费大量的时间来处理脚本里面的错误,重复的犯同样的,莫名奇妙的错误,却始终没有发现问题的罪魁祸首,其实是他们错误的认为自己需要“脚本语言”。
脚本语言,几乎永远是错误的决定。