65.9K
CodeProject 正在变化。 阅读更多。
Home

Angular 2 工具箱的 Shell 脚本

starIconstarIconstarIconstarIconstarIcon

5.00/5 (1投票)

2017年4月26日

CPOL

7分钟阅读

viewsIcon

10022

downloadIcon

86

本文介绍了我用于进一步自动化 Angular 2 应用程序开发的一些强大 shell 脚本。

引言

即使是最健壮、设计良好的开发环境,也几乎总能通过更多的自动化来改进。

当我跳过教程后,我很快就意识到,通过形式为 shell 脚本(Windows NT 命令脚本)的自动化,可以大大简化工作。

背景

在我漫长的开发生涯中,我一直致力于自动化许多重复的、容易出错的杂务,这些杂务会分散我处理手头任务的注意力,尤其是那些涉及必须按特定顺序完成的步骤的任务。起初,这种自动化 consisted of little IBM JCL (Job Control Language) decks that I kept in my personal code repository. 当我开始使用个人电脑时,那些 JCL deck 被批处理文件(现在有时称为 shell 脚本或 Windows NT Command Scripts)所取代。

在过去的大约 5 年里,为这些任务使用 PowerShell 已经变得很流行。尽管我非常擅长用 C# 编写程序,其中一些程序也被纳入了这个工具包,但 PowerShell 的学习曲线非常陡峭,而且不适合日常使用,除非你有现成的商业代码签名证书,并且愿意投入时间在所有脚本中使用它们。虽然存在变通方法,但一旦我能实现任何一种变通方法,我就可以写好、测试并投入生产我的 Windows NT 命令脚本了。

本文配套的包包含九个 shell 脚本和程序(32 位原生代码和托管代码的混合),我在 Angular 工作中使用它们,总结如下表。

工具集
FileName描述
Date2FN.exe将 LastWriteTime 追加到文件名。
LSNEWEST.BAT返回与通配符匹配的最新的文件名。其他几个脚本调用此脚本。
MAKEZIP_DAG.CMD使用 WinZip 或 PK-Zip 来创建目录的备份存档。
SaveChromeDeveloperConsoleLog.CMD从快捷方式运行此脚本,该快捷方式将其工作目录设置为您要收集 Chrome 控制台日志的目录。保存日志时,将其命名为 localhost.log;此脚本使每个文件名唯一。
StartAngularApp.CMD从快捷方式运行此脚本,该快捷方式将其工作目录设置为您创建 Angular 2 应用程序并希望对其进行工作的目录。该脚本将查找从 seed 中提取的应用程序源文件。
StartAngularDevWebServer.CMDCreateNewAngularApp.CMD 和 StartAngularApp.CMD 都使用此脚本来启动 Angular CLI 附带的 Web 服务器。由于我尚未完成测试,因此 CreateNewAngularApp.CMD 已省略。
WWPause.exe这个健壮的内部 PAUSE 命令的替代品,只响应回车符或 CTRL-C。
wwsleep.EXE此程序与 sleep.exe 类似,不同之处在于其内存占用量要小得多。

包中有两个基本相同的 Excel 文档。

  1. ToolboxInventory_Compact.XLSX 包含一个工作表,该工作表配置为仅显示您实际运行的脚本和程序。
  2. ToolboxInventory.xlsx 包含一个工作表,列出了包中的所有内容,包括辅助 DLL。

两个工作簿实际上包含相同的信息,但 ToolboxInventory_Compact.XLSX 隐藏了除您实际使用的程序之外的所有内容。我将留给您自己去发现我是如何做到的。

使用代码

大多数 Windows NT 命令脚本旨在连接到桌面快捷方式,利用它们的“启动 in”(工作目录)属性来设置解析相对路径引用的默认目录。

Date2FN.exe、 WWPause.exe 和 wwsleep.EXE 是通用的实用程序,可用于各种用途。我有许多其他脚本使用了这三个程序,而 Date2FN.exe 本身就是一个非常方便的命令行实用程序。

关注点

经典的批处理文件 LSNEWEST.BAT 相当直接。它最不寻常之处在于它通过环境变量 LSNEWEST 来接收输入和返回输出,并且它为两者使用了相同的变量。这主要是工程便利性的问题,因为环境变量是返回函数(子例程)结果的唯一可用设施。

dir "%LSNEWEST%" /b /od > %TEMP%\LSNEWEST.TMP
for /f %%i in (%TEMP%\LSNEWEST.TMP) do set LSNEWEST=%%i
del %TEMP%\LSNEWEST.TMP
  1. 环境变量 LSNEWEST 中的文件规范被输入到内部 dir 命令,该命令被指示返回按修改日期从旧到新排序的纯文件名列表。
  2. 列表从 %TEMP%\LSNEWEST.TMP 读取,然后 LSNEWEST 被更新为文件名,这样当控制跳出循环时,它将包含最新的文件名。
  3. 文件列表已完成其任务,将被丢弃。

%TEMP% 环境变量包含用户临时(临时)目录的名称,该目录保证是可写的。

您可能会将 SaveChromeDeveloperConsoleLog.CMD 连接到桌面快捷方式。除了将 LSNEWEST.BAT 视为子例程外,它还会调用一个内部子例程 fnglob,该子例程接受两个参数,如下所示。

call :fnglob LSNEWEST %SRCFN%

第一个参数是 LSNEWEST,一个字符串文字,而 SRCFN 存储第一个(也是唯一的)参数的值(如果存在),或者 Chrome 控制台日志的默认名称 localhost.log。当 fnglob 返回时,LSNEWEST 已被转换为同名的环境变量,其中包含 LSNEWEST.BAT 所需的 glob。

由于 SRCFN 在传递到子例程时会进行扩展,因此它会保留其正常的边界标记。

Date2FN.exe -r !SRCFN!
call LSNEWEST.BAT
start !LSNEWEST!

虽然未展开的字符串可以安全地传递到 fnglob,但接下来的三个命令需要由感叹号标记强制延迟展开。

函数 fnglob 大体上是直接的。

:fnglob <pglob> <pfilename>
(
    setlocal EnableDelayedExpansion
    set fnpath=%~dpn2
    set fnextn=%~x2
    set "fnglob=!fnpath!*!fnextn!"

    if "%_SaveChromeDeveloperConsoleLog%" equ "DEBUG" (
        echo.
        echo -------------------------
        echo Within fnglob:
        echo Arg 1  = %1
        echo Arg 2  = %2
        echo fnpath = !fnpath!
        echo fnextn = !fnextn!
        echo fnglob = !fnglob!
        echo -------------------------
        echo.
    )
)
(
    endlocal

    if "%_SaveChromeDeveloperConsoleLog%" equ "DEBUG" (
        echo.
        echo -------------------------
        echo Exiting fnglob:
        echo -------------------------
        echo.
        echo fnglob = %fnglob%
    )

    set "%~1=%fnglob%"
    exit /b
)

出现在函数名称旁边的两个字符串,<pglob> <pfilename>,是参数,批处理文件将其视为注释。

受另一个延迟展开的 setlocal 块的保护,fnpath 和 fnextn 分别被设置为直到扩展名的输入文件规范和扩展名本身。下一个命令构造成为第三个环境变量 fnglob 值的字符串。

If 块内的代码,if "%_SaveChromeDeveloperConsoleLog%" equ "DEBUG",会生成一系列调试消息,但前提是环境变量 _SaveChromeDeveloperConsoleLog 已定义且值为 DEBUG。 由于 _SaveChromeDeveloperConsoleLog 仅在需要时定义,因此此块什么也不做,除非您通过定义和设置它来启用它。

最后一个括号块中的倒数第二个语句,set "%~1=%fnglob%",负责通过定义一个名为第一个参数的第一个参数的环境变量,并将其值赋给本地环境变量 fnglob 来返回该值。必需加引号,但自从我发明这个技巧以来已经很久了,我早已忘记了原因

最后一个命令是 exit /b,其中 /b 指示命令处理器返回到调用脚本,类似于大多数“真正”编程语言中的 return不带参数的 exit 命令会退出整个脚本,而不是预期的结果!

exit /b 的替代方法是 goto :eof,其中 eof 是一个虚拟标签,存在于脚本的最后一行文本之后。我两者都使用,尽管我最近的子例程更倾向于使用 exit,因为我认为它更清晰一些。

我将用这些批处理文件中的每一个的几个特性来总结这一节。

@echo off
goto SKIPREM
  1. 每个脚本都以显示的两个命令开头,第一个命令抑制命令回显,包括它本身,下一行跳转到脚本标签 SKIPREM,这是一个我几乎总是使用的标准标签。
  2. 立即跳转到 SKIPREM 使我能够在脚本顶部包含一个漂亮的注释块,包括修订历史记录,并且它允许在脚本顶部包含任意数量的函数(子例程),而不是在末尾,这是一种常见的做法。
  3. SKIPREM 标签后的第一个命令始终是 echo BOJ %~0, version %~t0,它会宣布批处理文件的名称(如果有引号则去除),后跟一个由脚本文件最后修改日期组成的永久版本号。
  4. 我的大多数脚本(但不是全部)接下来会调用 ShowTime.CMD,它会显示当前日期和时间。它不是最完善的显示,因为它根本不调整字符串,但它很有效。
  5. 最后,StartAngularApp.CMD 和 StartAngularDevWebServer.CMD 多次调用 Node 包管理器和各种 node.js 包,这些包实际上是 Windows NT 命令脚本。为了防止它们过早终止,这些调用必须使用 CALL 命令。启动 Visual Studio Code 编辑器的 code 命令也是如此,它也是一个命令脚本。

历史

2017 年 4 月 26 日星期三是首次发布日期。

© . All rights reserved.