1799 年,一名法国陆军工程师取得了一项重大发现。不,不是鹅肝酱、卡门培尔奶酪、巴氏消毒法或沙特(Sartre)— 实际上,他发现了能够破译埃及古代象形文字的钥匙 —— 罗塞塔石碑(参见 图 1)。
图 1. 罗塞塔石碑,1100 磅重,其上使用三国语言篆刻了税收策略。碑文展示的是减免僧侣税款的诏书。
这块石碑制作于公元前 196 年,篆刻了对同一段文字的三种不同语言版本 — 分别是象形文字、通俗体文字(埃及草书)和希腊文字。通过对照翻译,或在不同语言版本之间寻找对应的词汇,罗塞塔石碑解读出已经失传已久的象形文字的含义。
换句话说,将罗塞塔石碑想像成 Babelfish。即使在公元前 196 年,就出现了使用一种以上的语言进行表达。
公元 2000 年末,软件开发人员面对着一个相似的问题。有太多的语言和方法可以用来表达同一内容。即使对于命令行,也有许多类似的内容可供选择,包括各种 shell 和不同的命令组合。
通常来讲,多样性是件好事,重庆打印机租赁,但是它也会让人觉得害怕。应该选择哪种解决方案?这种技术是否能够跟上需求的变化?时间和精力方面的投入能否得到回报?这些编写良好的代码(或 Perl 代码)是否会过时?更糟糕的是,是否需要针对其他环境转换(重写)所有内容?
如果您不希望局限于 Fish shell、Bash shell、Z shell、Windows operating system 的 cmd.exe 或其他一些 shell 脚本语言的特性,那么请尝试使用 Squirrel Shell。Squirrel Shell 提供了一种高级的、面向对象的脚本语言,在 Unix、Linux、Mac OS X 和 Windows 系统上都可以良好地运行。您只需要编写一次脚本,就可以在任意平台上运行。
#p#副标题#e#更妙的是,您需要做的工作非常简单。
获得 Squirrel
根据 GNU Public License version 3 (GPLv3) 的条款,Squirrel Shell 很容易获得并且可以免费使用。最新的版本为 2008 年 10 月 11 日发布的 1.2.2。Squirrel Shell 的创建者和维护者是 Constantin "Dinosaur" Makshin。
Squirrel Shell 的下载页面(参见 参考资料)提供了针对 32 位和 64 位 Windows 的源代码和二进制代码。如果您使用 Unix 或 Linux,请检查发行版附带的库,寻找合适的二进制文件或从头构建 Squirrel Shell。
从头构建 Squirrel Shell 非常简单。下载并提取源代码 tarball 文件,放到源代码目录,然后使用非常典型的构建 shell,如 清单 1 所示。
清单 1. 从头构建 Squirrel Shell
$ ./configure --with-pcre=system && make && sudo make install
Checking CPU architecture... x86
Checking for install... /usr/bin/install
...
Configuration has been completed successfully.
Build for x86 CPU architecture
Installation prefix: /usr/local
Allow debugging: no
Build static librarIEs
Use system PCRE 6.7 library
Install MIME information: auto
Create symbolic link: no
Compile C code with 'gcc'
Compile C++ code with 'g++'
Create static libraries with 'ar rc'
Create executables and shared libraries with 'g++'
Install files with 'install'
要查找与包有关的选项列表以进行配置,需在命令行中输入 ./configure --help。
为方便起见,Squirrel Shell 打包了 Perl Compatible Regular Expression (PCRE) 库的源代码,这些内容在程序中被大量使用。如果系统缺少 PCRE,打包后的代码可以使构建变得简单快捷。然而,如果系统已经有了 PCRE,那么可以通过指定 --with-pcre=system 选项来使用它。另一种方法是指定 --with-pcre=auto 以链接到更新的系统库或 Squirrel Shell 的副本。
构建的结果是得到一个新的二进制文件,名为 squirrelsh。假设此文件被安装到 PATH 变量的某个目录中,比如 /usr/local/bin,那么输入 squirrelsh 以启动该 shell。在命令行提示符下,输入命令 printl(getenv("HOME")); 以输出主目录的路径:
$ squirrelsh
> printl( getenv( "HOME" ) );
/home/strike
> exit();
Squirrel Shell 基于 Squirrel 编程语言(参见 参考资料 获得更多信息的链接)。该语言类似于 C++,并且提供了非常类似于 Python 和 Ruby 等面向对象脚本语言的特性。Squirrel Shell 纳入了 Squirrel 中的所有特性和数据类型,并添加了一些专门为常见 shell 脚本任务编写的新功能,比如复制文件和读取环境变量。
#p#分页标题#e#尽管 Squirrel Shell 的语法对于日常的命令行使用过于繁杂 —echo $HOME 是和 Squirrel Shell 的 printl( "~") 具有等效功能的 Bash 命令 — 但是它拥有出色的脚本。您只需要编写一次,就可以到处运行,而不需要针对 Unix 和 Windows 分别编写。正如 Dinosaur 这样评价他的工作,“Squirrel Shell 主要是充当一个脚本翻译器。
#p#副标题#e#使用 Squirrel 编写脚本
让我们看一看一个 Squirrel Shell 脚本的示例。清单 2 展示了文件 listing2.nut,此脚本将递归地列出您的主目录的内容。
清单 2. listing2.nut
#!/usr/bin/env squirrelsh
function reveal( filedir ) {
if ( !exist( filedir ) ) {
return;
}
if ( filename( filedir ) == ".." || filename( filedir ) == "." ) {
return;
}
if ( filetype( filedir ) == FILE ) {
printl( filename( filedir, true ) );
return;
}
printl("Directory: " + filename( filedir, true) );
local names = readdir( filedir );
foreach( index, name in names ) {
reveal( name );
}
}
local previous = getcwd();
chdir( "~" );
reveal( getcwd() );
chdir( previous );
exit( 0 );
按照规定,每个 shell 脚本的第一行将向操作系统表明要启动哪个程序来解释脚本。通常,这一行会显示 #! /usr/bin/bash 或 #! /bin/zsh 以从某个位置启动特定 shell 或解释器。
#!/usr/bin/env squirrelsh 有一些不同。它启动了一个特殊的程序 env,此程序又启动 PATH 变量中找到的第一个 squirrelsh 实例。因此,可以修改 PATH 变量以支持某个程序的本地版本 — 即您自己的、修改后的 squirrelsh 副本,位于 $HOME/bin/squirrelsh — 而不要修改 shell 脚本的内容。
#p#副标题#e#注意:这个技巧适用于所有解释器。例如,#!/usr/bin/env ruby 将按照 PATH 设置的指示,调用您喜欢的 Ruby 版本。总之,如果计划发布所编写的任何 shell 脚本,在第一行中使用 #!/usr/bin/env application 表单,因为它的 “移植性 更强:它将运行用户 在他/她的 PATH 变量中已经配置好的应用程序版本。
清单 2 的其余部分应该比较熟悉,至少对于方法是这样。函数 reveal() 是递归的:
如果为 reveal() 传递一个无效的路径或 “小圆点(.,当前目录)或 “两个小圆点(..,父目录),那么递归将结束。
否则,如果参数 filedir 是一个文件,代码将输出其名称并返回,并再一次停止进一步的递归。函数 filename() 可以接受一到两个参数。如果只有一个参数,或者第二个参数为 false,那么将忽略扩展文件名。如果提供 true 作为第二个参数,将返回完整的文件名。
如果参数是一个目录,代码将输出其名称,然后扫描内容(不需要执行深度优先处理,因为目录内容并没有按特定的顺序排列。下一个示例将改进输出)。
需要注意一点:由于对 reveal() 的调用是同一个函数中的最后一条语句,Squirrel 虚拟机(VM)— 运行脚本代码的引擎 — 可以通过称为尾递归(tail recursion)的技术将递归改为迭代。实际上,尾递归消除了对递归使用调用栈的需要;因此,可以实现任意深度的递归并且可以避免栈溢出。
Squirrel 的语法相当简单,因此使用这种语言编写代码非常快捷,特别是如果您曾经使用过 C、C++ 或任何更高级的语言编写过代码的话,这一点则体现得更充分。
最妙的是,这个 shell 代码是可移植的。将它转移到 Windows 机器上,在其上安装 Squirrel Shell,然后就可以运行您的代码。