squirrel 脚本

    技术2024-07-02  88

    1799年,一位法国陆军工程师做出了惊人的发现。 不,不是鹅肝,卡门培尔奶酪,巴氏杀菌法或萨特法,而是罗斯塔石,这是解密埃及大部分古代象形文字的关键(参见图1)。

    图1. Rosetta石头,一种1100英镑的三语税收政策。 碑文是对神职人员减税的公告。 (Hans Hillewaert信用2007)

    这块石头建于公元前196年,刻有一个段落的三种译本-每种用象形文字,Demotic(埃及文字)和古典希腊语。 通过比较翻译,或将短语从一种翻译映射到另一种,罗塞塔石碑揭示了许多曾经难以理解的字形的含义。

    换句话说,将Rosetta石头视为半吨重的Babelfish。 即使在公元前196年,也有不止一种方式来发表讲话。

    2000年后,软件开发人员面临着类似的问题。 在这么多的编程语言中,有很多方法可以说同一件事。 即使在命令行中,也有许多类似物可供选择,包括各种外壳和各种命令组合。

    一般来说,品种不错,但也可能令人生畏。 您选择哪种解决方案? 技术会跟上需求的步伐吗? 投入的时间和精力会有所回报吗? 还是那些整洁的字形(或那些Perl的标志?)会过时? 更糟糕的是,是否需要为其他环境翻译( 重写 )所有内容?

    如果您不想使用Fish Shell,Bash Shell,Z Shell,Windows操作系统的cmd.exe或某些其他Shell脚本语言的特性,请尝试Squirrel Shell。 Squirrel Shell提供了一种先进的,面向对象的脚本语言,该语言在UNIX,Linux,Mac OS X和Windows系统中同样有效。 您可以编写一次脚本,然后在任何地方运行。

    更好的是,您不必在耳朵上放半吨的石头即可使用它。

    捉松鼠

    Squirrel Shell随时可用,并且根据GNU公共许可证版本3(GPLv3)的条款免费使用。 最新版本是1.2.2,日期为2008年10月11日。Squirrel Shell的创始人和维护者是Constantin“ Dinosaur” Makshin。

    Squirrel Shell的下载页面(请参阅参考资料中的链接)提供了32位和64位Windows的源代码和二进制文件。 如果使用UNIX或Linux,请检查发行版的存储库中是否有合适的二进制文件,或者从头开始构建Squirrel Shell。

    从头开始构建非常简单。 下载并解压缩源tarball,转到源目录,并增加清单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兼容正则表达式(PCRE)库的源代码,该库在程序中广泛使用。 如果您的系统缺少PCRE,则捆绑的代码可使构建变得快速而简单。 但是,如果您的系统已经具有PCRE,则可以通过指定--with-pcre=system选项来选择使用它。 否则,指定--with-pcre=auto链接到系统库或Squirrel Shell副本的较新版本。

    构建的结果是一个新的二进制文件,恰当地命名为squirrelsh。 假设二进制文件安装在PATH变量的目录中,例如/ usr / local / bin,则键入squirrelsh启动外壳程序。 在命令提示符下,键入命令printl(getenv("HOME")); 打印主目录的路径:

    $ squirrelsh > printl( getenv( "HOME" ) ); /home/strike > exit();

    Squirrel Shell基于Squirrel编程语言( 有关更多信息的链接,请参阅参考资料 )。 该语言类似于C++ ,并提供类似于Python和Ruby等面向对象的脚本语言的功能。 Squirrel Shell包含了Squirrel中的所有功能和数据类型,并添加了许多专门为常见的Shell脚本编写任务而编写的新功能,例如复制文件和读取环境变量。

    尽管Squirrel Shell的语法在日常工作中过于冗长,但命令行使用printl( "~") echo $HOME是Squirrel Shell的printl( "~")的Bash等效项-它在脚本中printl( "~")用。 您只需编写一次,就可以在任何地方运行,而不能在UNIX中一次编写,而在Windows中则需要编写一次。 正如恐龙在谈到自己的作品时所说的:“松鼠壳主要是脚本解释器。”

    用松鼠编写脚本

    让我们看一个示例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 #! /usr/bin/bash或#! /bin/zsh 该行上的#! /bin/zsh可以从特定位置启动特定的Shell或解释器。

    A#!/usr/bin/env squirrelsh有点不同。 它启动一个特殊程序env,该程序又启动在PATH变量中找到的squirrelsh的第一个实例。 因此,您可以更改PATH变量以支持某些程序的本地版本(例如,您自己的修改后的squirrelsh副本在$ HOME / bin / squirrelsh中),而无需更改shell脚本的内容。

    注意:此技巧适用于各种口译员。 例如,根据您的PATH设置, #!/usr/bin/env ruby将调用您首选的Ruby版本。 通常,如果您打算分发编写的所有Shell脚本,请在第一行中使用#!/usr/bin/env application表单,因为它更“便于携带”:它将运行用户在其中配置的应用程序的版本他或她的PATH变量。

    清单2的其余部分应该看起来很熟悉,至少在方法上是这样。 函数reveal()是递归的:

    如果您传递了reveal()无效的路径或多年生的“点”( . ,当前目录)或“点。”( .. ,父目录),则递归结束。 否则,如果参数filedir是文件,则代码将打印其名称并返回,再次中止进一步的递归。 函数filename()可以使用一个或两个参数。 使用一个参数或第二个参数为false ,将省略文件名的扩展名。 如果提供true作为第二个参数,则返回整个文件名。 如果参数是目录,则代码将打印其名称,然后扫描其内容。 (该处理不一定是深度优先的,因为目录的内容不是按特殊顺序排序的。下一个示例改进了输出。)

    一件令人感兴趣的事情:因为对reveal()的调用是该函数中的最后一条语句,因此Squirrel虚拟机(VM)(运行脚本代码的引擎)可以通过称为尾部递归的技术将递归转换为迭代。 本质上,尾递归消除了调用堆栈的使用。 因此,可以进行任意深度的递归并避免堆栈溢出。

    Squirrel的语法非常稀疏,因此使用该语言编写代码会很快,尤其是如果您使用C , C++或任何更高级别的语言编写代码。

    最好的是,此shell代码是可移植的。 将其传输到Windows计算机,安装Windows的Squirrel Shell,然后运行您的代码。

    摆弄桌子

    Squirrel的一个不错的功能是相对于典型Shell而言,它丰富的数据结构集。 如果可以很好地组织数据,则通常可以快速解决复杂的问题。 Squirrel具有真实的对象,异构数组和关联数组(或表,用Squirrel说)。

    Squirrel表由插槽或(键/值)对组成。 除Null外的任何值都可以用作键; 可以将任何值分配给插槽。 使用“箭头”运算符( <- )创建一个新插槽。

    让我们改进清单2中的代码,以显示目录的内容,然后再深入到任何子目录。 该方法? 使用本地表在单独的插槽中累积文件和子目录,然后相应地处理这两个类别。 清单3显示了新版本的代码。

    清单3.清单2的增强版本,它首先打印目录的内容,然后递归到子目录中
    #!/usr/bin/env squirrelsh function reveal( filedir ) { local tally = {}; tally[FILE] <- []; tally[DIR] <- []; if ( !exist( filedir ) ) { return; } if ( filename( filedir ) == ".." || filename( filedir ) == "." ) { return; } local names = readdir( filedir ); foreach( index, name in names ) { tally[ filetype( name ) ].append( name ) ; } foreach( index, file in tally[FILE] ) { printl( file ); } foreach( index, dir in tally[DIR] ) { printl( filename( dir ) + "/" ); } foreach( index, dir in tally[DIR] ) { reveal( dir ); } } local entries = readdir( (__argc >= 2) ? __argv[1] : "." ); exit( 0 );

    表是在此处使用的理想数据结构。 reveal()的表有两个插槽:一个用于文件,一个用于目录。 该函数的返回值filetype( name ) -或者恒定FILE或恒定DIR -collat​​es在文件系统中的每个项目成其相应的插槽中。

    此外,每个插槽都是一个数组,由两个语句tally[FILE] <- []和tally[DIR] <- []; 。 ( []是一个空数组。)因为tally是函数内的局部变量,所以每次调用都会重新创建tally ,它会超出范围,并在每次调用返回时自动销毁。

    数组函数append( arg )将arg添加到数组的末尾,从而在进程中累积一个列表。 在循环foreach( index, name in names ) ,每一项都已放入一个插槽的列表中。 在函数中找到的其余代码将先打印文件,然后目录,然后递归。

    当然,如果没有命令行参数,shell脚本将毫无价值。 特殊的Squirrel Shell变量__argc和__argv包含命令行参数的计数和作为字符串数组的参数列表。 同样,根据约定, __argv[0]始终是外壳程序脚本的名称; 因此,如果__argc至少为2,则提供了其他参数。 为简便起见,此脚本仅处理第一个额外的参数argv[1] 。

    作为参考, 清单4显示了一个与清单3在功能上相同的Ruby脚本(由Makshin先生编写)。尽管Ruby简洁,但仍不如Squirrel Shell代码那么简洁。

    清单4. Ruby中清单3的重新实现
    !/usr/bin/ruby # List directory contents. path = ARGV[0] == nil ? "." : ARGV[0].dup # Remove trailing slashes while path =~ /\/$/ path.chop! end entries = Dir.open(path) for entry in entries unless entry == "." || entry == ".." filePath = "#{path}/#{entry}" fileStat = File.stat(filePath) if fileStat.directory? puts "dir : #{filePath}" elsif fileStat.file? puts "file: #{filePath}" end end end entries.close()

    有关Squirrel语言的详细信息,请参见《 Squirrel编程语言参考》 ( 有关链接,请参阅参考资料 )。

    巧妙地,Squirrel Shell中的几乎所有功能都抽象了底层操作系统的细节,因此您的代码可以尽可能通用。 例如, filename()函数(在上面的前两个清单中使用)将文件路径名中的前导路径剥离filename()例如,将/home/example/some/directory/file.txt减少为file.txt)您在什么平台上。 类似地, readdir()和filetype()允许您对真实的,基础的操作系统和文件系统的机密和陷阱一无所知。 通常,常规外壳程序不提供此类抽象。 (更高级的脚本语言可以。)

    其他有用的,独立convpath()平台的函数包括convpath()将路径名转换为本机路径名格式,以及run()调用另一个可执行文件。 convpath()函数是双向的,在编写跨平台脚本时非常有用。

    主要是正则表达式

    Shell脚本通常用于自动执行系统管理和维护工作。 这种自动操作背后的强大功能是正则表达式 ,这是一组真正的象形文字,用于查找,匹配和分解字符串。 如开头所述,Squirrel Shell需要PCRE库,该库也可以在Perl,PHP,Ruby和许多其他解释器和程序中找到。 PCRE是数据切片器和切块机的武士刀。

    尽管十分完善,但Squirrel Shell的正则表达式实现还是有些不同,可能会使您想起PHP的实现。 要在Squirrel Shell中使用正则表达式,请定义正则表达式,对其进行编译,进行比较,然后对结果进行迭代(如果有)。

    清单5显示了一个示例程序,该程序演示了Squirrel Shell中的正则表达式(该代码由Makshin先生编写,并经许可使用)。

    清单5. Squirrel Shell中的正则表达式演示
    #!/usr/bin/env squirrelsh // Match a regular expression against text print("Text: "); local text = scan(); print("Pattern: "); local pattern = scan(); local re = regcompile(pattern); if (!re) { printl("Failed to compile regular expression - " + regerror()); exit(1); } local matches = regmatch(re, text); if (!matches) { printl("Failed to match regular expression - " + regerror()); regfree(re); exit(1); } regfree(re); printl("Matches found:"); foreach (match in matches) printl("\t\"" + substr(text, match[0], match[1]) + "\"");

    在这里, scan()从标准输入中读取一些文本和模式,尽管没有通常界定正则表达式开始和结束的前导和尾部斜杠( / )字符。

    给定一个模式,函数reqgcompile()编译该模式,从而加快重复匹配的速度。 您可以使用reqgcompile()函数的标志(等效于PCRE /i修饰符)来启用或禁用区分大小写,并且可以将一行与另一行进行匹配以使用另一选项(与PCRE /m选项等效)。 如果不编译正则表达式,则所有匹配都将失败。

    regmatch(re, text)函数将正则表达式与文本进行比较,生成无匹配项的Null或整数对数组(双元素数组)。 任何一对中的第一个整数是比赛的开始; 第二个整数是比赛的结尾。 这解释了代码的最后一行中substr(text, match[0], match[1])的用法。

    执行比较后,可以遍历结果。 如果在任何时候不再需要编译后的正则表达式,请使用regfree() 。 还有一个regfreeall()函数,用于处理所有已编译表达式所拥有的所有资源。

    松鼠壳的螺母

    在理想的情况下,相同的编程逻辑将适用于UNIX,Linux和Windows,并且程序员即使没有更高的生产力也会更加快乐。 operating,操作系统有所不同,有时您必须诉诸特定系统的自定义代码。

    在那些既不能使用Squirrel Shell也不可以抽象平台的情况下,Squirrel Shell提供了一种方便的功能来探测操作系统,因此代码可以遵循适当的分支。

    清单6显示了如何使用platform()函数进行决策。 尽管此函数可能是unknown值,但它始终会返回一个值。

    清单6. platform()函数产生操作系统的类型
    print( "Made by ... "); local platform = platform(); switch ( platform ) { case "linux": printl( "Linus." ); break; case "macintosh": printl( "Steve." ); break; case "win32": case "win64": printl( "Bill." ); break; default: printl( "Unknown" ); }

    您还可以通过Squirrel Shell环境变量PLATFORM查找当前平台的类型:

    > printl( PLATFORM ); linux

    环境变量CPU_ARCH产生编译了外壳程序的处理器:

    > printl( CPU_ARCH ); x86

    啊,疯了!

    Squirrel Shell函数的其余部分管理文件,操纵环境和执行数学运算。 确实,三角函数有近20个内置函数。 现在正在计划2.0版,它将包括更多类,对Unicode的支持,改进的交互模式以及模块化的插件体系结构。

    松鼠壳不是交互式壳,但这没关系。 该类别中已经有许多替代品。 松鼠壳作为脚本运行者要好得多。 它的数据结构比传统的shell更有能力,它的语法很熟悉,其底层虚拟引擎支持从枚举类型到线程的所有内容。 Squirrel引擎也很小,不到6000行代码。 您甚至可以将所有Squirrel嵌入到另一个应用程序中。

    当您必须为两个平台编写代码时,请尝试使用Squirrel Shell。 它的坚果将帮助您保持健康。


    翻译自: https://www.ibm.com/developerworks/aix/library/au-spunix_squirrel/index.html

    相关资源:e语言-易语言Squirrel松鼠脚本支持库
    Processed: 0.011, SQL: 9