<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE article PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<article id="Win32Scripting">
  <articleinfo>
    <title>Win32 Shell Scripting Tutorial</title>
    <author>
      <firstname>Ashley</firstname>
      <othername>J.S</othername>
      <surname>Mills</surname>
      <affiliation>
        <address><email>ashley@ashleymills.com</email></address>
      </affiliation>
    </author>

    <copyright>
      <year>2005</year>
      <holder role="mailto:ashley@ashleymills.com">The University Of Birmingham</holder>
    </copyright>
  </articleinfo>
  <sect1 id="Win32Scripting-Introduction"><title>Introduction to Win32 Shell Scripting</title>
    <para>
      Time is precious.  It is non-sense-ical to waste time typing a frequently used sequence of commands at a command prompt, more especially if they are abnormally long or complex.  Scripting is a way by which one can alleviate this necessity by automating these command sequences in order to make ones life at the shell easier and more productive.  Scripting is all about making the computer, <emphasis role="strong">the tool</emphasis> do the work.  Hopefully by the end of this tutorial you should have a good idea of the kinds of scripting languages available for Windows and how to apply them to your problems.
    </para>
  </sect1>

  <sect1 id="Win32Scripting-TheEnvironment"><title>The Environment</title>
    <para>
      The environment is an area of memory associated with the command processor that provides upto 32KB of space for storing variables, the variables contain information about the operating environment that is used by the operating system and other programs in various ways, typically to inform a program of the location of a certain piece of information it requires.  A few examples follow:
    </para>

    <itemizedlist>
      <listitem>
        <para>
          <envar>ComSpec</envar>, specifies the location of the command interpreter.
        </para>
      </listitem>
      <listitem>
        <para>
          <envar>PATH</envar>, specifies the locations to search for commands typed at the command line.
        </para>
      </listitem>
      <listitem>
        <para>
          <envar>Prompt</envar>, specifies how the command prompt should appear to the user.
        </para>
      </listitem>
      <listitem>
        <para>
          <envar>AGE</envar>, a user-defined variable for indicating the age of something.
        </para>
      </listitem>
      <listitem>
        <para>
          <envar>TEMP</envar>, specifies the directories where temporary files should be placed.
        </para>
      </listitem>
    </itemizedlist>

    <para>
      Sometimes it is necessary to modify certain environment variables, for instance, we may have installed a new program in a directory such as <filename>C:\tools\chipper\</filename>, and its executable files may be stored in <filename>C:\tools\chipper\bin\</filename>.  We may desire to execute this program from the command line but find that we get an error saying something like:
    </para>

    <programlisting>
'chipper' is not recognized as an internal or external command, operable program or batch file.
    </programlisting>

    <para>
      Because we had not updated the <envar>PATH</envar> to point to the location of the binary files for this program.
    </para>

    <para>
      Altering environment variables will depend on what version of Windows you are using, see <ulink url="../winenvars/winenvarshome.html"><citetitle>Configuring A Windows Working Environment</citetitle></ulink>.
    </para>
  </sect1>

  <sect1 id="Win32Scripting-BatchProgramming"><title>Batch Programming</title>
    <para>
      A batch file is a plain ASCII text file with the file extension <filename>.bat</filename>, it is interpreted by the command processor, usually <filename>command.com</filename> or <filename>cmd.exe</filename>. Batch files are used to automate repetitive command sequences in the command shell environment.
    </para>

    <para>
      In the context of batch programming, the environment is exploited for it's ability to store text strings within definable variables known as environment variables as illustrated in the above example, these variables can be used in batch files in much the same way as you would use variables in other programming languages albeit with less freedom. The language used in batch files is usually referred to as a scripting language but it is in-fact Turing-complete hence is actually a programming language.
    </para>

    <sect2 id="Win32Scripting-BatchProgramming-AuxilliaryFiles"><title>Auxiliary files for extended batch programming</title>
      <para>
        Unfortunately, due to reasons unknown, many useful tools for batch programming have been omitted in recent versions of Windows and are only available from within costly 'resource kits' that one has to purchase for seemingly extortionate sums of money.  However, there are free alternatives to some of these tools and/or hacks/kludges to circumvent their necessity.
      </para>

      <sect3 id="Win32Scripting-BatchProgramming-AuxilliaryFiles-olddos"><title><filename>OldDos.exe</filename></title>
        <para>
          <ulink url="ftp://ftp.microsoft.com/softlib/mslfiles/olddos.exe">ftp://ftp.microsoft.com/softlib/mslfiles/olddos.exe</ulink>
        </para>
        <para>
          This is a self-extracting archive containing some of the files that were distributed with versions of Windows prior-to and including Windows 95, programs such as <command>qbasic</command>.
        </para>
      </sect3>

      <sect3 id="Win32Scripting-BatchProgramming-AuxilliaryFiles-ntreskit"><title><filename>NT Resource Kit</filename></title>
        <para>
          <ulink url="http://www.microsoft.com/ntserver/nts/downloads/recommended/ntkit/default.asp">http://www.microsoft.com/ntserver/nts/downloads/recommended/ntkit/default.asp</ulink>
        </para>
        <para>
          This is a subset of the tools found in the full Windows NT Resource Kit. Only available for Windows 2000/NT/XP users.
        </para>
      </sect3>

      <sect3 id="Win32Scripting-BatchProgramming-AuxilliaryFiles-choice"><title><filename>CHOICE.EXE</filename></title>
        <para>
          <filename>CHOICE.EXE</filename> is a batch command which allows the requisition of a choice from a basic set
          of choices presented to the user.
        </para>
      </sect3>
    </sect2>

    <sect2 id="Win32Scripting-Basics"><title>Batch Basics</title>
      <sect3 id="Win32Scripting-Command-Redirection-And-Pipelines"><title>Command Redirection and Pipelines</title>
        <para>
          If you want to get help on a command in Windows, the usual way is to postfix the command with a space and then <emphasis role="strong">/?</emphasis>.
        </para>
        <para>
          By default a normal command accepts input from standard input, which we abbreviate to stdin, standard input is the command line in the form of arguments passed to the command.  By default a normal command directs its output to standard output, which we abbreviate to stdout, standard output is usually the console display. For some commands this may be the desired action but other times we may wish to get our input for a command from somewhere other than stdin and direct our output to somewhere other than stdout.  This is done by redirection:
        </para>

        <itemizedlist>
          <listitem>
            <para>
              We use <command>&gt;</command> to redirect stdout to a file, for instance, if we wanted to redirect a directory listing generated by the <command>ls</command> we could do the following:
            </para>

            <screen><userinput><command>ls</command> <command>&gt;</command> <replaceable>file</replaceable></userinput></screen>
          </listitem>

          <listitem>
            <para>
              We use <command>&lt;</command> to specify that we want the command immediately before the redirection symbol to get its input from the source specified immediately after the symbol, for instance, we could redirect the input to <command>grep</command>(which searches for strings within files) so that it comes from a file like this: 
            </para>

            <screen>
              <userinput><command>grep</command> <replaceable>searchterm</replaceable> <command>&lt;</command> <replaceable>file</replaceable></userinput>
            </screen>
          </listitem>

          <listitem>
            <para>
              We use <command>&gt;&gt;</command> to append stdout to a file, for instance, if we wanted to append the current directory listing to the end of a file we could redirect the output from <command>dir</command> like so:
            </para>

            <screen>
              <userinput><command>dir</command> <command>&gt;&gt;</command> <replaceable>file</replaceable></userinput>
            </screen>
          </listitem>

          <listitem>
            <para>
              We can redirect standard error (stderr) to a file by using <command>2&gt;</command>, if we wanted to redirect the standard error from <command>commandA</command> to a file we would use:
            </para>

            <screen><userinput><command>commmandA</command> <command>2&gt;</command></userinput></screen>
          </listitem>
        </itemizedlist>

        <para>
          Pipelines are another form of redirection that are used to chain commands so that powerful composite commands can be constructed, the pipe symbol <emphasis role="strong">'|'</emphasis> takes the stdout from the command preceding it and redirects it to the command following it:
        </para>

        <screen><userinput><command>dir</command> <command>|</command> <command>more</command></userinput></screen>

        <para>
          The example above firsts requests a directory listing of the current directory using the <command>dir</command> command, the output from this is then piped to <command>more</command> which displays the results in a page by page basis, pausing after each screen full until the user presses a key.
        </para>
      </sect3>

      <sect3 id="Win32Scripting-Variables"><title>Variables</title>
         <sect4 id="Win32Scripting-Variables-Internal"><title>Variables</title>
           <para>New variables can be instantiated like this:</para>

           <screen>
             <userinput><command>set</command> <replaceable>name</replaceable>=<replaceable>value</replaceable></userinput>
           </screen>

           <para>
             The name must only be made up of alphabetic characters, numeric characters and underscores, it cannot begin with a numeric character.  You cannot use keywords like <emphasis>for</emphasis> as variable names.
           </para>

           <para>
             Variables are referenced like this: <emphasis role="strong">%</emphasis><replaceable>name</replaceable><emphasis role="strong">%</emphasis>, here is an example:
           </para>

           <programlisting>
@echo off
msg1=Hello
msg2=There!
echo %msg1% %msg%
           </programlisting>

           <para>This would echo &quot;Hello There!&quot; to the console display, here is another example:</para>

           <programlisting>
@echo off
set msg1=one
set msg2=%msg1% two
set msg3=%msg2% three
echo %msg3%
           </programlisting>

           <para>
             Would echo &quot;one two three&quot; to the screen. If you would like to echo the '%' character, escape it with another '%' like this '%%'. You can echo to another file to create a new batch file if you like:
           </para>

           <programlisting>
@echo off
echo set msg=Hello World! &gt; hello.bat 
echo echo %%msg%% &gt;&gt; hello.bat
hello.bat
           </programlisting>

           <para>
             This would cause &quot;set msg=Hello World!&quot; to be echoed and redirected to the file <filename>hello.bat</filename>, &quot;echo %msg%&quot; is then echoed and redirected to the file <filename>hello.bat</filename> but this time appended to the end. Notice the double '%' in the program-listing so that the '%' character is echoed and not interpreted as being the opening of a variable name. The final line executes <command>hello.bat</command> causing it output &quot;Hello World!&quot;.
           </para>
         </sect4>

         <sect4 id="Win32Scripting-Variables-CommandLine"><title>Command Line Arguments</title>
           <para>
             Command line arguments are treated as special variables within the batch script, the reason I am calling them variables is because they can be changed with the <command>shift</command> command.  The command line arguments are enumerated in the following manner <emphasis>%0</emphasis>, <emphasis>%1</emphasis>, <emphasis>%2</emphasis>, <emphasis>%3</emphasis>, <emphasis>%4</emphasis>,  <emphasis>%5</emphasis>,  <emphasis>%6</emphasis>, <emphasis>%7</emphasis>, <emphasis>%8</emphasis> and <emphasis>%9</emphasis>. <emphasis>%0</emphasis> is special in that it corresponds to the name of the batch file itself.  <emphasis>%1</emphasis> is the first argument, <emphasis>%2</emphasis> is the second argument and so on.  To reference after the ninth argument you must use the <command>shift</command> command to shift the arguments 1 variable to the left so that  <emphasis>$2</emphasis> becomes <emphasis>$1</emphasis>, <emphasis>$1</emphasis> becomes <emphasis>$0</emphasis> and so on, <emphasis>$0</emphasis> gets scrapped because it has nowhere to go, this can be useful to process all the arguments using a loop, using one variable to reference the first argument and <command>shifting</command> until you have exhausted the arguments list.
           </para>
         </sect4>
       </sect3>


      <sect3 id="Win32Scripting-Control-Constructs"><title>Control Contructs</title>
        <para>
          The flow of control within <acronym>batch</acronym> scripts is essentially controlled via the <emphasis role="strong">if</emphasis> construct.
        </para>

        <sect4 id="Win32Scripting-Control-Constructs-If"><title>If</title>
          <para>
            This construct takes the following three generic form, the parts enclosed within '[' and ']' are optional:
          </para>

          <programlisting>
IF [NOT] ERRORLEVEL number command
IF [NOT] string1==string2 command
IF [NOT] EXIST filename command
          </programlisting> 

          <para>
            When a Windows command exits it exits with what is known as an <emphasis>error level</emphasis>, this indicates to anyone who wants to know the degree of success the command had in performing whatever task it was supposed to do, usually when a command executes without error it terminates with an exit status of zero.  An exit status of some other value would indicate that some error had occurred, the details of which would be specific to the command. This is what the error-level <emphasis role="strong">if</emphasis> is for.
          </para>

          <programlisting>
@echo off
if &quot;%1&quot;==&quot;1&quot; echo The first choice is nice
if &quot;%1&quot;==&quot;2&quot; echo The second choice is just as nice 
if &quot;%1&quot;==&quot;3&quot; echo The third choice is excellent 
if &quot;%1&quot;==&quot;&quot;  echo I see you were wise enough not to choose, You Win!
          </programlisting>

          <para>
            What this example does is compare the first command line argument with the strings &quot;1&quot;, &quot;2&quot; and &quot;3&quot; using <emphasis role="strong">==</emphasis> which compares two strings for equality, if any of them match it prints out the corresponding message. If no argument is provided it prints out the final case. If you want to include more than one command, enclose it within braces:
          </para>

          <programlisting>
@echo off                          
if exist hello.bat (
   echo Removing hello.bat...
   del  hello.bat
   echo hello.bat removed!
)
          </programlisting>

          <para>Deletes hello.bat if it exists and prints acknowledgments</para>
        </sect4>

        <sect4 id="Win32Scripting-Control-Constructs-For"><title>For</title>
          <para>
            The syntax of the for command is:
          </para>

          <programlisting>
FOR %%variable IN (set) DO command [command-parameters]
          </programlisting>

          <programlisting>
@echo off
if exist bigtxt.txt rename bigtxt.txt bigtxt
for %%f in (*.txt) do type %%f &gt;&gt; bigtxt
rename bigtxt bigtxt.txt
          </programlisting>

          <para>
            In this example first of all we check to see if the file <filename>bigtxt.txt</filename> exists, if it does we rename it to bigtxt so that it is not included within the scope of the next line.  The second line says that for every <emphasis role="strong">%%f</emphasis> in <emphasis role="strong">(*.txt)</emphasis>, which is every <filename>*.txt</filename> file in the current directory, we should perform the command <command>type</command> on the files (which prints the file to stdout) and then append these to the file <filename>bigtxt</filename>.  The third line simply renames the file <filename>bigtxt</filename> to <filename>bigtxt.txt</filename>.
          </para>

          <para>
            A more useful option of the <emphasis role="strong">FOR</emphasis> construct is to use the option <option>/L</option> which turns in to like a numeric mode whereby the following syntax applies:
          </para>

          <programlisting>
FOR /L %%variable IN (start,step,end) DO command [command-parameters]
          </programlisting>

          <para>Here is an example:</para>

          <programlisting>
@echo off
FOR /L %%i IN (0 2 100) DO echo %%i
          </programlisting>

          <para>
            Which will simply, echo all the numbers in between zero and 100 inclusive, incrementing by 2 each time.  If you want to include more than one statement, use brackets after the <emphasis role="strong">DO</emphasis> part:
          </para>

          <programlisting>
@echo off
FOR /L %%i IN (0 2 100) DO (
   echo ***
   echo %%i
   echo ***
)            
          </programlisting>

          <para>
            To echo some lines above and below each number.
          </para>
        </sect4>

        <sect4 id="Win32Scripting-Control-Constructs-Loops"><title>Loops</title>
          <para>
            The batch language is so brain dead one has to employ ad-hoccery to get anything useful done, as an example I will illustrate how to create a simple counter known as a bang counter:
          </para>

          <programlisting>
rem A Bang Counter
@echo off
set n=%1
set i=
: loop
set i=%i%!
  echo Bang! 
  if %i%==%n% goto end
goto loop
:end
          </programlisting>

          <para>
            This takes a string of bangs, e.g &quot;!!!&quot;, as an argument and then echos &quot;Bang!&quot; for each bang in the string. The loop compares the string of bangs to the string <emphasis role="strong">i</emphasis> which is set up and loops while the two strings are not equal, on each loop iteration a bang is added to <emphasis role="strong">i</emphasis> hence the loop will iterate for the number of bangs in the argument string. One could use this to control the number of times some action was executed. If this is ran with no arguments the program loops forever because the bangs will never match.
          </para>
        </sect4>
      </sect3>
    </sect2>

    <sect2 id="Win32Scripting-BatchProgramming-SimpleExamples"><title>Simple Examples</title>

      <sect3 id="Win32Scripting-BatchProgramming-SimpleExamples-Alias"><title>Creating Aliases</title>
        <para>
          If you always use the same options to a command or would like to create an alias for a command with a certain set of options, why not use a batch file to do so, simply create the batch file and place it somewhere in your path. Make sure that you do not create a batch file with the same name as an actual command unless you really want to override the command if the batch file comes earlier on in the <envar>PATH</envar>. A few examples are shown below:
        </para>

        <programlisting>
@echo off
REM dir.bat, maps dir to Unix ls
dir %1
        </programlisting>

        <programlisting>
@echo off
REM antlr.bat, creates shortcut for antlr java program
java antlr.Tool %1
        </programlisting>

        <programlisting>
@echo off
REM home.bat, changes to your home directory and lists contents
c:
cd \home
cls
dir
        </programlisting>
      </sect3>
    </sect2>

    <sect2 id="Win32Scripting-BatchProgramming-examplebatchfile"><title>Example batch file</title>
      <para> 
        The example below illustrates some of the features of batch programming.
      </para>

      <example id="Win32Scripting-BatchProgramming-example-concatandzip"><title>Batch file - Concatenate and zip Example</title>
        <programlisting>
@echo off <co id="concatandzip-echooff"/>
:: A batch file that concatenates all the files of a given extension in <co id="concatandzip-comment"/>
:: the current directory to a specified file and then zips that file.
IF &quot;%1&quot;==&quot;&quot; GOTO USAGE <co id="concatandzip-errorhandle"/> 
IF &quot;%2&quot;==&quot;&quot; GOTO USAGE 
FOR %%i IN (*.%1) DO type %%i &gt;&gt; %2 <co id="concatandzip-forloop"/>
SHIFT <co id="concatandzip-shift"/>
pkzip %~n1.zip %1 <co id="concatandzip-pkzip"/>
GOTO END 
:USAGE <co id="concatandzip-label"/>
echo.
echo USAGE: %0 extension outputfile <co id="concatandzip-output"/>
echo.
echo Concatenates all the files of the given extension in the current directory
echo to the specified outputfile and then compresses that file into a zip file.
echo.
:END
        </programlisting>
      </example>

      <calloutlist>

        <callout arearefs="concatandzip-echooff">
          <para/>
          <programlisting>
@echo off
          </programlisting>

          <para>
            The command <command>echo</command> followed by the option <option>off</option> sets batch echoing to
            off, this means that the batch commands will not be echoed to standard output, that is, you will not
            see them on your screen. The line is prefixed by a &quot;@&quot; character which is used to specify
            that the line it is present on should not be echoed to standard output, hence is needed so that we
            do not echo the command sequence that turns echoing off.
          </para>
        </callout>

        <callout arearefs="concatandzip-comment">
          <para/>
          <programlisting>
:: A batch file that concatenates all the files of a given extension in
:: the current directory to a specified file and then zips that file.
          </programlisting>

          <para>
            Comments in batch files are officially meant to begin with the prefix <emphasis role="strong">REM</emphasis> 
            which stands for <emphasis role="strong">REM</emphasis>ark, this tells the command interpreter to ignore this line.  
            It turns out, however, that it is quicker to use <emphasis role="strong">::</emphasis> in order to 
            markup a comment.  <emphasis role="strong">::</emphasis> is used because <emphasis role="strong">:</emphasis> 
            indicates the start of a label, the next  <emphasis role="strong">:</emphasis> tells the parser to
            ignore this label.
          </para>
        </callout>

        <callout arearefs="concatandzip-errorhandle">
          <para/>
          <programlisting>
IF &quot;%1&quot;==&quot;&quot; GOTO USAGE
IF &quot;%2&quot;==&quot;&quot; GOTO USAGE
          </programlisting>

          <para>
            In batch programming, command line parameters are referenced by using the <emphasis role="strong">%n</emphasis> 
            notation where <emphasis role="strong">n</emphasis> indicates the numerical weight of the parameter on the 
            command line.  <emphasis role="strong">%0</emphasis> refers to the name of the batch file itself, 
            <emphasis role="strong">%1</emphasis> the
            first parameter, <emphasis role="strong">%2</emphasis> the second and so on.  
            You may only reference a maximum of 9 parameters in this way, if you
            need more you should process these first ones and then use the <command>shift</command> command to access the
            rest, the <command>shift</command>, shifts the parameters left by one so that <emphasis role="strong">%2</emphasis>
            becomes <emphasis role="strong">%1</emphasis> etc, hence the non-existent <emphasis role="strong">%10</emphasis> 
            would be shifted into <emphasis role="strong">%9</emphasis> and you could access the parameter. 
            There is no way to recover a parameter once it has been shifted out completely, i.e 
            <emphasis role="strong">%0</emphasis> will disappear upon being <command>shift</command>ed.
          </para>

          <para>
            The program listing illustrates the use of the <emphasis role="strong">IF</emphasis> clause to take an 
            alternative course of action if no either or both of the required parameters is missing.  
            <emphasis role="strong">%1</emphasis> and <emphasis role="strong">%2</emphasis> are compared to an empty string
            which would indicate their omission from the command line if true, hence if the 
            <emphasis role="strong">IF</emphasis> clause evaluates to <emphasis role="strong">TRUE</emphasis> the 
            command after is executed which is <emphasis role="strong">GOTO USAGE</emphasis> which indicates that
            program control should jump to the <emphasis role="strong">USAGE</emphasis> label. 
          </para>
        </callout>


        <callout arearefs="concatandzip-forloop">
          <para/>
          <programlisting>
FOR %%i IN (*.%1) DO type %%i &gt;&gt; %2
          </programlisting>

          <para>
            This for construct enables us to implement iterative behaviour in our batch files.  It says that for each
            <emphasis role="strong">%%i</emphasis> within the set <emphasis role="strong">*.%1</emphasis> we should perform 
            the command <emphasis role="strong">type %%i &gt;&gt; %2</emphasis>.  
          </para>
            
          <para>
            <emphasis role="strong">%%i</emphasis> is a for-loop 
            variable, these begin with <emphasis role="strong">%%</emphasis> and end with a sequence of alpha characters. 
            The for-loop says to assign the values in our set to this variable in turn,
            which allows us to reference each of the values in the set with the same variable in the for-loop body allowing
            us to perform a specific operation on each of the values in the set.  The specific operation in this case
            is to redirect the output of <command>type</command> (which dumps the contents of a file to stdout) to a file,
            specifically <emphasis role="strong">%2</emphasis> which is our second command line parameter. 
            The use of two <emphasis role="strong">&gt;</emphasis> characters in series ensures
            that the contents of the file are appended to the end of the target file and do not overwrite the output file,
            which would be the case if only one <emphasis role="strong">&gt;</emphasis> had been used.
          </para>
        </callout>

        <callout arearefs="concatandzip-shift">
          <para/>
          <programlisting>
SHIFT
          </programlisting>

          <para>
            The shift command shifts all command line parameters one parameter to their left, see number 3 for more details about shift.  The reason it has been used here is explained in the next callout.
          </para>
        </callout>

        <callout arearefs="concatandzip-pkzip">
          <para/>
          <programlisting>
pkzip %~n1.zip %1
          </programlisting>

          <para>
            Pkzip is a shareware file compression program that is executed from the command line, for details on obtaining 
            PkKzip see <link linkend="Win32Scripting-BatchProgramming-AuxilliaryFiles">
              <citetitle>Auxiliary files for extended batch programming</citetitle></link>, it takes the command form:
          </para>

          <screen>PKZIP zipfile file(s)...</screen>

          <para>
            Where <emphasis role="strong">zipfile</emphasis> is the name of the zipfile you wish to create and 
            <emphasis role="strong">file(s)...</emphasis> are the files you want to put in the zipfile.  
            In order that the zipfile be given the same name as the second parameter (without the extension) passed to 
            the batch file, the notation <emphasis role="strong">%~n1</emphasis> was used to specify that 
            <emphasis role="strong">%~n1</emphasis> should take the value of the name of 
            <emphasis role="strong">%1</emphasis> but not the extension.  The notation 
            <emphasis role="strong">%2~n1</emphasis> is not allowed so to reference just the filename of the second 
            parameter passed to the batch file and not the extension, the command line parameters were first shifted to the
            left by one using <command>SHIFT</command>, as explained previously, the second parameter became the first 
            parameter and it's filename could be referenced with <emphasis role="strong">%~n1</emphasis>.  
            Pkzip was told to use the filename 
            <emphasis role="strong">%~n1.zip</emphasis> as the zipfile and <emphasis role="strong">%1</emphasis> as the file to
            compress, which (owing to the <command>SHIFT</command>) was the second parameter passed to the batch file.
          </para>
        </callout>


        <callout arearefs="concatandzip-label">
          <para/>
          <programlisting>
:USAGE
          </programlisting>

          <para>
            Labels are specified by prefixing an alpha-character identifier with a 
            <emphasis role="strong">':'</emphasis> character, labels are jumped to using the <command>GOTO</command>.
          </para>
        </callout>

        <callout arearefs="concatandzip-output">
          <para/>
          <programlisting>
echo.
echo USAGE: %0 extension outputfile
echo.
echo Concatenates all the files of the given extension in the current directory
echo to the specified outputfile and then compresses that file into a zip file.
echo.
          </programlisting>

          <para>
            By using <command>echo</command> text may be echoed to standard output so that the user may see it. 
            <command>echo.</command> echos an empty line to standard output.  This section of the program is
            only executed if one or both of the command line parameters are omitted.  The program shows that
            normally execution will be caused to circumvent this section by means of 
            <emphasis role="strong">GOTO END</emphasis> immediately prior its declaration.
          </para>
        </callout>
      </calloutlist>
    </sect2>

    <sect2 id="Win32Scripting-BatchMaker-Example"><title>BatchMaker example - Unix for Windows (sort of)</title>

      <note><para>This works on Windows 2000, I have tested this on Windows 98 and it does not work.</para></note>

      <para>
        I was fed up of accidentally typing <command>ls</command> instead of <command>dir</command> at the DOS prompt to get a directory listing so I started making some batch files to map the DOS commands like, <command>move</command>, <command>dir</command>, <command>copy</command> etc. to their Unix equivalents. For example, here is the mapping for <command>copy</command> to <filename>cp.bat</filename>:
      </para>
        
      <programlisting>
@echo off                               
if &quot;%1&quot;==&quot;&quot; GOTO USAGE
copy %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO END
:USAGE
copy /?
:END
      </programlisting>
        
      <para>
        Which just see's if there is a command line argument present, if there is not, it prints the usage instructions for the command, otherwise it executes the command with the maximum number of command line arguments (9) (just in case). Most of the mappings I wanted to do had this similar format, only varying in the number of required arguments, this is the main drawback. For instance <command>ls</command> (dir) takes no arguments so you would not want to print the usage instructions every time somebody tried to get a directory listing (the person would not even get their directory listing).  What I could have done was use some kind of contrived kludge of a counter, a bang counter or something and let the person specify the number of command line arguments on the command line but I wasn't prepared to waste my time doing so when this behaviour suited the majority of commands I had to match. OK, I might take a look at it when I have nothing better to do... like never ;) This got tedious after a while so I thought it would be a good idea to make a command to do the mappings for me.
       </para>

       <para>
          All I had to do to create a batch file that generated this template was make it echo that template to another file, changing the words &quot;copy&quot; to whatever the command is and do various other things, here it is:
       </para>

       <programlisting>
@echo off
if &quot;%1&quot;==&quot;&quot; GOTO USAGE
if &quot;%2&quot;==&quot;&quot; GOTO USAGE

echo @echo off
FOR /L %%i IN (1 1 %2) DO (
  echo if &quot;%%%%i&quot;==&quot;&quot; GOTO USAGE
)

echo %1 %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9
if &quot;%2&quot;==&quot;0&quot; GOTO END
echo GOTO END
echo :USAGE
echo %1 /?
echo :END
GOTO END

:USAGE
echo maps a command to an alias
echo map %%1 %%2 
echo %%1 is command to map
echo %%2 is the number of command line args the command *needs*
echo e.g map copy 2 &gt; cp.bat (to map copy to cp.bat)
:END
      </programlisting>

      <para>
        When referring to echoing, this will occur to stdout but the command is used by redirecting the output to a file so what is being echoed will end up in another batch file. This means the things being echoed are batch commands.  The batch file first checks that it's own command line arguments are present, if they are not it echos it's command usage.  If the command line args <emphasis>are</emphasis> present, it first echoes:
      </para>

      <screen>@echo off</screen>
       
      <para>
        Because this will be present at the top of all of the batch files created. It then enters this loop:
      </para>

      <programlisting>
FOR /L %%i IN (1 1 %2) DO (
  echo if &quot;%%%%i&quot;==&quot;&quot; GOTO USAGE
)       
      </programlisting>

      <para>
        Which starts at <emphasis role="strong">1</emphasis> and iterates by <emphasis role="strong">1</emphasis>until <emphasis role="strong">%%i</emphasis> exceeds the number specified as the second command line argument, so if <emphasis role="strong">1</emphasis> is specified as the second argument the body of the loop will be executed once, echoing:
      </para>

      <screen>if &quot;%1&quot;==&quot;&quot; GOTO USAGE</screen>

      <para>
        &quot;<emphasis role="strong">%%%%i</emphasis>&quot; echos <emphasis role="strong">%</emphasis> followed by the value of <emphasis role="strong">%%i</emphasis>, the loop counter. So this loop creates the checks for the command line arguments that the command being mapped should have.  If zero is specified as the second command line argument, the loop is never executed which is the desired action since a command with zero arguments needs no argument checking.
      </para>

      <programlisting>echo %%1 %%2 %%3 %%4 %%5 %%6 %%7 %%8 %%9</programlisting>

      <para>Echoes</para>

      <screen>command %1 %2 %3 %4 %5 %6 %7 %8 %9</screen>

      <para>
        So that the command will then be executed (in the generated file) with the maximum possible number of arguments, this is so that when the command is executed, any redirection and args etc. will be executed too, otherwise the command would not do anything.
      </para>

      <programlisting>
if &quot;%2&quot;==&quot;0&quot; GOTO END
echo GOTO END
echo :USAGE
echo %1 /?
echo :END
GOTO END
      </programlisting>

      <para>
        The batch then checks if the number of required arguments for the command being mapped was zero, if it was then there is no need to echo the rest of the stuff because a command that needs zero arguments should generate a file like this: 
      </para>

      <programlisting>
@echo off
copy %1 %2 %3 %4 %5 %6 %7 %8 %9
      </programlisting>

      <para>
        If the required arguments for the command being mapped is not zero then the rest of the stuff is echoed, the rest of the stuff just ensures that if the mapped command is ran with not enough arguments, that command's usage instructions will be printed by calling the generic DOS help function of the command with the <emphasis role="strong">/?</emphasis> switch. The last line skips the usage instructions of <filename>map.bat</filename> so that the batch terminates.
      </para>

      <para>
        An example usage will clarify, the contents of <filename>mv.bat</filename> after executing <screen><userinput><command>map</command> move 2 &gt; <filename>mv.bat</filename></userinput></screen> are:
      </para>

      <programlisting>
@echo off
if &quot;%1&quot;==&quot;&quot; GOTO USAGE
if &quot;%2&quot;==&quot;&quot; GOTO USAGE
move %1 %2 %3 %4 %5 %6 %7 %8 %9
GOTO END
:USAGE
move /?
:END
      </programlisting>

      <para>
        So of course you can redirect the batch file to somewhere in your path or whatever. Here is another quick batch file that generates some aliases and puts them in a directory of your choice:
      </para>

      <programlisting>
@echo off
if &quot;%1&quot;==&quot;&quot; GOTO USAGE

call map copy 2 &gt; %1\cp.bat
call map move 2 &gt; %1\mv.bat
call map del  1 &gt; %1\rm.bat
call map type 1 &gt; %1\cat.bat 
call map dir  0 &gt; %1\ls.bat

GOTO END

:USAGE
echo Creates a set of Unix-named aliases for common DOS commands:
echo map %%1
echo where %%1 is the output directory, e.g c:\windows\command
:END
      </programlisting>

      <para> 
        Notice that the keyword <emphasis role="strong">call</emphasis> is used to call the <emphasis role="strong">map</emphasis>, this is necessary so that after <emphasis role="strong">map</emphasis> has finished, program control will return to the next statement in this batch file.  If call had not been used then only the first statement would have executed because the batch would have terminated after <emphasis role="strong">map</emphasis> had.  Feel free, of course, to add any other commands you want to.
      </para>
    </sect2>
  </sect1>
</article>

