好吧,首先承认我这篇文章有标题党的嫌疑,这篇文章的标题应该是:Windows平台各常用脚本语言对比分析&放弃批处理的n个理由。
不过无妨了,只要能忽悠你们点进来就行,哈哈。
一、学习-收益曲线。
上图是我绘制的Windows常用脚本语言的学习成本-收益曲线。
1.学习成本与学习收益。
学习成本可理解为学习时间,学习收益,则是你学习了一段时间后所能实现的项目、实现项目速度、项目运行表现、以及项目跨平台可用性的综合量化描述。
2.曲线分析。
从上图可知,学习初期,批处理的收益是最高的,而随着学习的深入,学习收益却大幅降低。
这就是我们常说的:批处理入门容易,深入难。
而且我还要加上一句:深入批处理,你会领略到批处理语法的恶心之处,并且不会学到什么实用的技能——除了让你的批处理BUG变得少一些。
曲线的最右端,则是你花相同的时间精通各语言后收益的对比。
而Take Command,只需要你有基本的批处理基础,便可以以极低的学习成本达到比拟VBS的学习收益。
至于绘制这个图像的理由,我将在下文做分析。
二、批处理有什么问题?
在这个标题中,我会破除一些大家对CMD、批处理的“迷信”。
经常关注批处理之家QQ群的朋友,应该知道老刘我时不时会在群中对想要学习批处理的新人进行劝退。而我每次都会举出以下理由:
1、命名混乱。
熟悉批处理的朋友,应该知道批处理的命令命名没有什么规律,有些是几个单词的首字母简写,有些是一个单词的头几个字母,有些则没有实际的意义。
这其实也有历史的原因,因为批处理在dos时代就已经出现。为了保证对之前编写脚本的兼容性,很多命令的命名、语法都参照、借鉴了dos时代的先例。而dos是个只支持8.3命名的系统——即要求文件名最多只能有8个大写英文字符作为文件名、最多3个作为拓展名。这个特性天然导致了早期dos命令的简短。到了windows时代,随着对系统外壳功能要求的提升,微软陆陆续续的添加了不少外部命令,而很遗憾,这些命令也没有遵循统一的命名标准。
这会导致什么后果?高昂的学习成本。
想要实现功能,你需要知道并记住相关的命令名称,而不规则的命名就会增加你的学习开销。
这方面Take Command其实也是反例,但是相比批处理,它具有完善的手册,并且对命令进行了分类,这在部分程度上弥补了这一缺点。
正面的例子是VBScript和PowerShell。
先说VBS,VBS的内部函数命名也较为混乱,但还算有迹可循——如C开头的往往是类型转换的函数,B结尾的往往是以字节为单位处理输入的函数,函数名也多以单词命名,可以望文生意。VBS的外部库——各种系统COM组件,其属性、方法命名都是较为规范的,往往是表达该属性、方法的意义的单词的拼接。这样的命名本就适宜望文生意,再配合上支持事实显示类信息的编辑器(如vbsEdit、PrimalScript),可以快速且高效的使用而不用翻阅手册。
PowerShell可以说是正面例子中的正面例子,其命令统一以动词-名词的规范命名,望文生意简直不要太舒服,而其最常用的.NET库也有自己一套完善的命名规则和参考手册。Windows还自带一个非常好用的ISE(脚本编辑调试器),可以快速检索需要的命令。
2、参考、教程良莠不齐。
批处理的教程很多,但良莠不齐。
微软的官方教程——即HELP质量和各命令的/?参数,也是机器翻译,而且因为FOR中翻译的种种错误,被广为诟病。
其它的教程我也多多少少看过,多数讲的很浅,而且缺乏对批处理机制的理解,但也有不少优秀的教程,如中华兄的《重定向与句柄》、批家的《FOR从入门到精通》、BatchDebugger作者的批处理入门教程,等等。
我在这里说这些,并不是推荐大家找来学,因为批处理现在已经没有太多学习的必要,这些教程虽通俗易懂,深入浅出,但毕竟还是在讲述一个处于末年的、出生时就有先天缺陷的、畸形的批处理啊!
批处理的教程互相之间也缺乏必要的索引,很多知识都是程序出了BUG,在解决BUG的路上学到的,这对新手非常的不友好,所以我看到很多人的批处理水平都停留在比较初级的水平,实话说这也怪不得他们。
VBS的教程同样很杂,网上的教程同样良莠不齐(点名批评简明教程,有很多误导性的语法使用),不过好在微软官方的参考手册还做的可以,虽然中文版本的参考手册中缺了很多新版本中加入的函数。参考手册配合上vbsEdit的F1帮助功能,可以快速查找到函数、语法的帮助。
PowerShell教程不多,其中PsTips(PowerShell中文博客)的入门教程很好,而且在搜索结果中排在较前的位置。微软也提供了获得参考的命令Get-Help,虽然参考是英文。
python的文章很多,同样良莠不齐。不过也有很多比较正规的学习网站,其中py教程的质量还是很高的。
3、语法奇诡、语素缺失。
现在的很多语言,为了降低使用者的学习成本,语法都会向现有的语言靠拢。
而批处理不管是处于历史原因或者如何,是反其道而行的。它有一套自己独立的语法,而且还是那种“四不像”,语法解析的机制微软本身也没有解释清楚,导致了很多BUG的出现、以及批处理的各种奇技淫巧。
批处理的语法还有很多人工修补的痕迹,如解决变量展开问题的变量延迟,FOR中的usebackq、等等,如此种种都反映出了批处理的一个问题——先天不足(甚至畸形)。
一个语言的产生,很重要的部分就是语言规范的指定。如果规范定的不周全,不仅表达能力会有限制,还会产生一些难以解决的问题,如语言语法的冗余、缺失。
题外话,如果大家学过AHK,应该知道AHK v1的语法和批处理有一定的相似,而且AHK的语法风格比以上提过的所有语言更为混乱(批处理+类VB+类C混杂),AHK项目官方在v2版本进行了部分更改,但由于不兼容部分v1的语法,导致很多使用v1语法编写的库无法正常使用,这也使v2遭受冷遇。这也说明语言语法规定应该有自己统一的标准,并且最好小心谨慎,不要留下什么缺陷。
批处理的语法是基于对字符串的处理,这个机制也使其在处理所谓“特殊字符”的问题上捉襟见肘,并且限制了其对其它数据类型的处理能力。
批处理的语素缺失是极为严重的,如果你从其他语言入门,可能还好,但如果你从批处理入门,你多半不熟悉以下几个概念:多条件跳转、条件循环、函数、数组、变量类型、表达式与语句、递归、迭代、函数式编程、等等。
虽然这些语素可以在批处理中通过构造一些结构实现,但这本身就限制了你编程思维的表达。从批处理入门的人更不会知道:哇!原来程序还可以这么写!
这方面TC其实也是反例,因为其函数只能组合TC已有的函数,灵活性还远远不达其他语言函数的水平。
批处理对语言排版的限定较为宽松,这个特性有优点也有缺点,但我认为缺点是大过优点的。这个特性给了你灵活排版代码的可能性,但是也导致很多缺乏基本编程素养的新手,不进行代码的缩进、变量名的意义化、甚至将代码都写在一行中,写出了不是给人看的代码。
这个时代,编译器已经为你做了很多,代码往往偏重于让人看懂,而不是机器。不好的代码规范会加剧代码的阅读成本、也会让新手掉入垃圾代码的深渊。
这个方面的一个例子是python,它将缩进也作为语法的一部分,强制使用者编写观感良好的代码,实际证明这个决定是很成功的。
4、底层封装不足,高层过度封装。
用过批处理的人都知道,批处理处理单个字符是很麻烦的,需要将其用变量截取从一整个字符串中取出来。这就是所谓的底层封装不足。
底层不足还体现在这些方面:字符串处理函数没有、变量类型没有、二进制处理的相关函数库没有、数学运算很弱、等等等等。
而批处理的语法特性又使得这个技巧只有那些掌握了预处理机制(变量展开顺序)的人才能写出。这就造成了高昂的学习成本,并且劝退了很多新手。
高层过度封装的意思是:批处理的命令往往都能直接实现一个较为复杂的功能,但是其并不是简单命令的堆砌,而是直接当头一棒,给你一个完整的产品。
这就好比,你要用积木搭一座自己的城堡,其它语言的积木都是小块的、易于拼合的、各色各样的,而批处理的是一些整块,还有一些小碎渣。你想要搭建城堡,就需要将一整块尽可能的打磨成自己需要的形状,辅以无数的碎渣,勉强做出需要的模样。
这样的特性,有优点吗?有的,当你的需求正好符合那些“整块”时,可以直接使用,非常简洁。但是只要略有变化,你就得废老大劲了,而且效果还不一定好。很多需求就干脆的做不出来。
这个方面,其它语言都有较好的实现,TC也为批处理语法拓展了很多函数,如单字符处理、二进制内存区域读写等。
5、运行速度缓慢。
这个也是批处理非常大的弊病,你说批处理难用就难用,要是像汇编一样运行那么快,那也无妨了,但是问题是批处理运行的极慢,甚至可以说慢出了天际。
为什么慢呢?这个还得从批处理的运行机制讲起。批处理的运行方法是:读一句(注意不是一行),执行一句。而且自带热更新功能。你问什么是热更新功能?就是说你在本地修改脚本,正在运行的脚本会立马做出改变,而不用重新启动。
这个特性同样是双刃剑,而且同样是弊端的那边更锋利一些。
我们知道,内存内数据的读写速度是硬盘的几万倍,特别是随机读写。而批处理的这个机制就导致批处理老是需要从硬盘上读取东西,这就给硬盘增加了很大的开销,也拖慢了批处理的运行速度。批处理中引用一个外部命令,CMD会从Path的每个目录寻找,这一过程同样非常耗费时间。
批处理的变量展开机制也导致了批处理会频繁解析自身的语句,甚至相同的命令也要每次调用parser(解析器),这导致批处理运行的开销非常大。
这个问题TC也做了优化,比原生的批处理强了一些。
VBS的语句除了Execute、Eval外,都是在运行前就解析产生好二进制代码的,并且不用老是找硬盘麻烦,所以速度还挺快。
PowerShell则因为其基于.NET,所以正常编写速度也不太快,需要做特殊的优化才能提速。
python虽然本身速度也不快,不过有很多项目(如Cython)支持将其翻译成C语言,而且一些库(如Numpy、cupy)也对运行速度做了优化,所以这方面的问题还不是很大。
6、函数库良莠不齐,且不灵活。
批处理有函数库?其实根本没有。
我说函数库那是拿第三方来撑场面。(此处脑补斜眼笑表情)
这个还是底层封装不足的问题,Import、封装概念的缺失导致根本没什么人给批处理写库,而且更没什么人用、而且就算写了也慢出天际。
就算是第三方命令行程序,由于批处理的运行机制(第三方运行、返回结果、关闭),也导致第三方非常的不灵活,比如第三方在运行过程中不能和批处理交互,而运行完了环境也无法保存。
第三方的语法也是千奇百怪,没有统一的标准,这导致其学习成本同样很高。
而且网络上对于第三方的引导也非常有限,导致很多新手都不知道什么情况下该用什么第三方,甚至第三方的分类、功能也不清楚。
所以就会看到一群人,每天使用着一个什么字符串函数都没有、连正则表达式都不支持的语言,却做着字符串处理的任务。而且每每遇到相同的功能,都会勤劳的重写一遍,而不是封装一个函数重复使用。
这点TC做了很大的拓展,增添了很多基本的命令、函数和变量,可以说极大的方便了程序的编写。
而VBS的第三方库也不少,如著名的大漠插件,可以使VBS模拟鼠标键盘、进行窗口操作、编写游戏辅助变得非常方便,DWX插件,可以使VBS完美调用系统API(VBS的语法和DWX配合可以支持回调、结构体等)。
PowerShell坐拥庞大的.NET库,功能不要太丰富,还支持调用系统API、批处理外部命令、第三方命令行程序、以及VBS唯一能用的COM库,可以说是非常灵活、强大。
不过前面举的几个例子比起python都是小巫见大巫,py的库出名的多、而且种类也多,功能还丰富。python已经广泛应用在网络爬虫、人工智能等领域。
7、无法跨平台。
想一想,你写了一段代码,在所有的主流系统上都能运行,是多么舒服的一件事。
不过显然批处理是做不到的,TC也做不到。
其它语言也各有不同。如果你想在Mac上运行powershell脚本,需要装约300M的.NET运行库。python则与你使用的库有关。VBS在安卓系统上被按键精灵实现,而在其它系统只能靠转换为类似的语言(如FreeBasic)来实现跨平台。
三、总结。
看了上面的分析,相信你已经对这些语言和其特性有了基本的理解,我的建议如下。
如果你还没有学过批处理,我建议直接学习PowerShell、Python。
如果你学过了一些/深入学习了批处理,可以考虑轻松入门TC,之后可以考虑其它语言。
如果你学习了VBS,我建议转向JScript或PowerShell,有很多相似之处,也更有学习价值。