Introduction
Even the most robust, well designed development environments almost always can stand a bit more automation.
As I moved past the tutorial, it took me almost no time to realize that I could significantly streamline the work with a dose of automation, in the form of shell scripts (Windows NT command scripts).
Background
Throughout my long development career, I have endeavored to automate as many of the repetitive, error prone chores that take my attention away from the task at hand, especially those that involve steps that must be completed in a specific order. In the beginning, this automation consisted of little IBM JCL (Job Control Language) decks that I kept in my personal code repository. When I started working with personal computers, those JCL decks were replaced with batch files (now sometimes called shell scripts or Windows NT Command Scripts).
Over the last 5 years or so, it has become fashionable to use PowerShell for these kinds of tasks. Although I am quite confortable writing programs in C#, some of which found their way into this tool kit, PowerShell has a very steep learning curve, and is poorly suited to everyday use, unless you have ready access to commercial code signing certificates, and are willing to invest the time to use them in all of your scripts. Workarounds exist, but, by the time I could implement any of them, I could have my Windows NT command script written, tested, and in prodduction.
The package that accompanies this article contains nine shell scripts and programs (a mix of 32 bit native and managed code) that I use in my Angular work, summarized in the following table.
Tools in the SetFileName | Description |
---|
Date2FN.exe | Append LastWriteTime to file name. |
LSNEWEST.BAT | Return the name of the newest file that matches a wildcard specification. Several other scripts call this script. |
MAKEZIP_DAG.CMD | Use either WinZip or PK-Zip to make a backup archive of a directory. |
SaveChromeDeveloperConsoleLog.CMD | Run this script from a shortcut that sets its working directory to the directory where you want your collection of Chrome console logs. When you save the log, name it localhost.log; this script makes each file name unique. |
StartAngularApp.CMD | Run this script from a shortcut that sets its working directory to the directory where you created the Angular 2 application on which you want to work. The script will find the application source files extracted from the seed. |
StartAngularDevWebServer.CMD | Both CreateNewAngularApp.CMD and StartAngularApp.CMD use this script to launch the Web server that comes with the Angular CLI. Since I haven't finished testing it, CreateNewAngularApp.CMD is omitted. |
WWPause.exe | This robust replacement for the internal PAUSE command answers only to a Carriage Return or a CTRL-C. |
wwsleep.EXE | This program is similar to sleep.exe, except that its memory footprint is quite a bit smaller. |
There are two substantially identical Excel documents in the package.
ToolboxInventory_Compact.XLSX
contains one worksheet that is configured to show only the scripts and programs that you actually run. ToolboxInventory.xlsx
contains one worksheet that lists evertyhing in the package, including the helper DLLs.
Both workbooks actually contain the same information, but ToolboxInventory_Compact.XLSX
hides everything except the programs that you actually use. I'll leave you to discover for yourself how I did that.
Using the code
Most of the Windows NT command scripts are intended to be wired up to desktop shortcuts that leverage their Working Directory ("start in") properties to set the default directory against which to resolve relative path references.
Date2FN.exe
, WWPause.exe
, and wwsleep.EXE
are general purpose utility programs that can be put to all sorts of uses. I have many other scripts that use all three, and Date2FN.exe
is pretty handy on its own as a command line utility.
Points of Interest
Classic batch file LSNEWEST.BAT
is fairly straightforward. Its most unusual aspect is that it takes its input and returns its output through an environment variable, LSNEWEST
, and that it uses the same varible for both. This is mostly a matter of engineering convenience, since environment variables are the only facility avaiilble for returning results from a function (subroutine).
dir "%LSNEWEST%" /b /od > %TEMP%\LSNEWEST.TMP
for /f %%i in (%TEMP%\LSNEWEST.TMP) do set LSNEWEST=%%i
del %TEMP%\LSNEWEST.TMP
- The filespec in environment string
LSNEWEST
is fed to the internal dir
command, which is instructed to return a bare listing of file names, sorted from oldest to newest by modified date. - The list is read from
%TEMP%\LSNEWEST.TMP
, and LSNEWEST
is updated with the file name, so that it contains the name of the newest file when control falls through the loop. - The file list has done its job, and is discarded.
The %TEMP%
environment variable holds the name of the user's temporary (scratch) directory, which is guaranteed to be writeable.
You will probably wire SaveChromeDeveloperConsoleLog.CMD
up to a desktop shortcut. In addition to treatomg LSNEWEST.BAT
as a subroutine, it calls an internal subroutine, fnglob
, which takes two parameters, as follows.
call :fnglob LSNEWEST %SRCFN%
The first argument is LSNEWEST
, a string literal, while SRCFN
stores the value of the first (and only) argument, if any, or a default name for the Chrome console log, localhost.log
. When fnglob
returns, LSNEWEST
has been transformed into an environemnt string of the same name that holds the glob that LSNEWEST.BAT
needs.
Since it gets expanded as it passes into the subroutine, SRCFN
, keeps its normal bounding tokens.
Date2FN.exe -r !SRCFN!
call LSNEWEST.BAT
start !LSNEWEST!
While the unexpanded strings may be safely passed into fnglob
, the next rhee commands require the delayed expansions imposed by the exclamation point tokens.
Function fnglob
is mostly straightforward.
: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
)
The two strings that appear in angle brackets next to the function's name, <pglob> <pfilename>
, are parameters, which the batch file treats as comments.
Protected by another expansion delayed setlocal
block, fnpath
and fnextn
are set to the input filespec up to the extension and the extension, itself, respectively. The next command constructs the string that becomes the value of the third environment variable, fnglob
.
The code within the If
block, if "%_SaveChromeDeveloperConsoleLog%" equ "DEBUG"
, generates a series of debugging messages, but only when environment variable _SaveChromeDeveloperConsoleLog
is defined and has a value of DEBUG
. Since _SaveChromeDeveloperConsoleLog
is defined only as needed, this block does nothing, unless you turn it on by defining and setting it.
The next to last statement in the final parenthetical block, set "%~1=%fnglob%"
, is responsible for returning the value by defining an environment variable with the name given in the first argument, and assigning to it the value of local environment variable fnglob
. The enclosing quotations are required, but it's been so long since I devised this trick that I have long since forgotten why.
The last command is exit /b
, where the /b
instructs the command processor to return to the calling script, similar to return
in most "real" programming languages. Unadorned, the exit command exits the entire script, not the intended outcome!
An alternative to exit /b
is goto :eof
, where eof
is a virtual label that exists just past the last line of text in the script. I use both, although my more recent subrotines tend toward exit
, because I think it's a tad clearer.
I'll wrap this section up with a couple of features of every one of these batch files.
@echo off
goto SKIP
-
Every script begins with the two commands shown above, the first of which suppresses command echo, including itself, and the next line goes to a script label, SKIPREM
, a standard label that I almost always use.
- Jumping immediately to
SKIPREM
allows me to include a nice block of comments at the top of the script, including a revision history, and it permits any number of functions (subroutines) to be included at the top of the script, rather than at the end, as is a common proactice. - The first command following the
SKIPREM
label is always echo BOJ %~0, version %~t0
, which announces the name of the batch file, stripped of enclosing quotation marks, if any, followed by an evergreen version number comprised of the last modified date of the script file. - Most, but by no means all, of my scripts next call
ShowTime.CMD
, which displays the current date and time. It's not the most polished display, because it doesn't bother to tweak the string at all, but it's effective. - Finally,
StartAngularApp.CMD
and StartAngularDevWebServer.CMD
make several calls into the Node Package Manager and variisous node.js packages, which happen actually to be Windows NT command scripts. To prevent their premature termination, these invocations must use CALL
commands. The same goes for the code
command that starts the Visual Studio Code editor, also a command script.
History
Wednesday, 26 April 2017 is the initial publication date.