Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How Command Line Parameters Are Parsed #4248

Open
guevara opened this issue Jun 6, 2019 · 0 comments
Open

How Command Line Parameters Are Parsed #4248

guevara opened this issue Jun 6, 2019 · 0 comments

Comments

@guevara
Copy link
Owner

guevara commented Jun 6, 2019

How Command Line Parameters Are Parsed



http://bit.ly/2qbdpax



About 0 Minutes


How Command Line Parameters Are Parsed

by

David Deley

© 2009
(Updated 2014)
(Updated 2016)


How to escape the special characters in your parameters

Contents:



bar

*nix

(Unix, Linux, Ubuntu, Kubuntu, ...)19


1.  How a new process is created on *nix
On *nix, you launch a program using the execve API (or it's cousins execvp, execl, execlp, execle, and execvp). The interesting thing about these APIs is that they allow the caller to specify each of the command line arguments - the signature for execve is:

     int execve(const char *filename, char *const argv [], char *const envp[]);


2.  Passing Parameters to a new process on *nix
On *nix, the parameters are parsed off by whatever program creates the new process, before the new process is created.1

*nix
cmdline = "MyProg  Hello  Goodbye  Friday"          
 ↓ parse          
argv[] = MyProg.exe
Hello
Goodbye
Friday
         
 ↓ create new process          
execve(argv[])       New Process running MyProg.exe has argv[]  

2.1  Example 1: Bash Shell
If you launch a program using the Bash shell command line, The Bash shell is responsible for turning the string provided by the user into the argv[] argument vector which is then passed to the program. For the Bash shell parameter parsing rules, the following links provide some information:

2.2  Example 2: Ubuntu Desktop Launcher Icon (GNOME→Nautilus)
In Ubuntu a desktop launcher icon can be created by right-clicking on the desktop and selecting "Create Launcher". This will generate a GNOME launch file ~/Desktop/launcher-name.desktop This is a text file you can browse (have a look at one). In this text file is a line starting with Exec= where the command and any command line parameters are.17

So what happens when you double-click on this desktop icon?

Ubuntu uses the GNOME Desktop Environment, which in turn uses the Nautilus File Manager. When you double-click on a desktop launcher icon, the Nautilus File Manager parses the Exec= command line in the ~/Desktop/launcher-name.desktop file and calls execve passing it argv[]. So to know how your command line parameters are parsed you need to look into how Nautilus parses command lines. There's some good documentation here:

2.3  Example 3: Kubuntu (KDE→Plasma)
Kubuntu is based on Ubuntu but uses the KDE Plasma Desktop instead of GNOME. So, to determine how your command line is parsed in that environment, you'll need to look into KDE application launchers. This starts to get complicated, because according to the documentation there are three: Kickoff, classic K menu, or Lancelot Launcher.18

It gets even more complicated, because it's possible to install both the KDE Plasma Desktop (kubuntu-desktop) as well as the GNOME desktop (ubuntu-desktop) on the same machine.



3.  The *nix Parameter Parsing Rules
It all depends on who is processing your command line before the new process is created. On *nix, the parameters are parsed off by whatever program creates the new process.

This is all I have to say about *nix. The rest is about Windows.





bar


Windows®


1.  How a new process is created on Windows
On Windows, a new program is launched by calling the CreateProcess() API, which takes the command line as a string (the lpComandLine parameter to CreateProcess):

     int CreateProcess( ..., lpComandLine, ... )


2.  Passing Parameters to a new process on Windows
On Windows, the parameters are parsed off the command line after the new process is created, by the new process. It's considered the responsibility of the newly started application to call the GetCommandLine() API to retrieve the command line string and parse it (possibly using the CommandLineToArgvW() helper function).2

Windows
cmdline = "MyProg.exe  Hello  Goodbye  Friday"          
 ↓ create new process          
CreateProcess(cmdline)       New Process running MyProg.exe  
         ↓ call  
        GetCommandLine()  
         ↓ parse  
       
argv[] = MyProg.exe
Hello
Goodbye
Friday
 


3.  How does a C/C++ program on Windows get argv[]?
The C/C++ compiler which compiles the program secretly adds extra code to the executable that retrieves and parses the command line to extract the parameters before calling WinMain (or main). Thus, for a C/C++ executable on Windows, the parameter parsing rules are determined by the C/C++ compiler that compiled the program.


4.  Everyone Parses Differently
You'll get different results if you pass a command line to ShowParams.exe (written in C/C++), ShowParams.vbs (VBScript), or ShowParams.bat (batch file):

Sample Code:
  • ShowParams.c
    • #include "stdafx.h"
      int main(int argc, char* argv[])
      {
        for (int i = 0; i < argc; ++i)
        {
          printf("param %d = ",i);
          puts(argv[i]);
          printf("\n");
        }
        return 0;
      }


      (Note: Avoid using printf when printing parameters, as the parameter may contain a % which would be interpreted by printf as a conversion specifier. The program may crash with a bizarre error, such as "runtime error R6002 - Floating point not loaded".)



      (Note: If we started this function with wmain or _tmain then argv[] would point to wide character (unicode) strings. [Technically I'm not sure if they are UTF-16LE or UCS-2. I have 2 old posts discussing the basics of Unicode:

      I also wrote a short book on Unicode:

  • ShowParamsC#.cs
    • using System;

      namespace ShowParams
      {
          class Program
          {
              static void Main(string[] args)
              {
                  Console.WriteLine("There are {0} program arguments", args.Length);

                  foreach (string arg in args)
                  {
                      Console.WriteLine(arg);
                  }
              }
          }
      }
  • ShowParams.bat
    • @echo off
      echo Param1 = %1
      echo Param2 = %2
      echo Param3 = %3
  • ShowParams.vbs
    • If Wscript.Arguments.Count = 0 Then
              Wscript.echo "No parameters found"
      Else
          i=0
              Do until i = Wscript.Arguments.Count
              Parms = Parms & "Param " & i & " = " & Wscript.Arguments(i) & " " & vbcr
              i = i+1
              loop
              Wscript.echo parms
      End If
Results:
  1. ShowParams.exe
    ShowParams.exe (written in c)
    >ShowParams.exe "c:\test a\" "c:\test b\"
    param 0 = ShowParams.exe
    param 1 = c:\test a" c:\test
    param 2 = b"
    ( \" is interpreted as meaning 'a literal double quote.')

  2. C#ShowParams.exe
    C#ShowParams.exe (written in C#)
    >C#ShowParams.exe "c:\test a\" "c:\test b\"
    There are 2 program arguments
    c:\test a" c:\test
    b"
    ( \" is interpreted as meaning 'a literal double quote.')

  3. ShowParams.bat
    ShowParams.bat (a batch file)
    >ShowParams.bat "c:\test a\" "c:\test b\"
    param 1 = "c:\test a\"
    param 2 = "c:\test b\"
    (the double quotes are included as part of the parameter)

  4. ShowParams.vbs
    ShowParams.vbs (a Visual Basic script)
    >ShowParams.vbs "c:\test a\" "c:\test b\"
    param 0 = c:\test a\
    param 1 = c:\test b\
    (The double quotes are removed from the parameter)
Diagrams:
  1. ShowParams.exe
    ShowParams.exe     and     C#ShowParams.exe
    cmdline = "ShowParams.exe Hello Goodbye Friday"          
     ↓          
    CreateProcess(cmdline)       New Process running ShowParams.exe  
             ↓ call  
            GetCommandLine()  
             ↓ parse  
           
    argv[] = ShowParams.exe
    Hello
    Goodbye
    Friday
     

    The parameter parsing rules are determined by the C++ compiler that compiled ShowParams.exe



  2. ShowParams.bat
    ShowParams.bat
    cmdline = "ShowParams.bat Hello Goodbye Friday"          
     ↓          
    CreateProcess(cmdline)       New Process runs cmd.exe to process the batch file ShowParams.bat  
             ↓ call  
            GetCommandLine()  
             ↓ parse  
            %1 = Hello
    %2 = Goodbye
    %3 = Friday
     

    The parameter parsing rules are determined by cmd.exe which processes the batch file.



  3. ShowParams.vbs
    ShowParams.vbs
    cmdline = "ShowParams.vbs Hello Goodbye Friday"          
     ↓          
    CreateProcess(cmdline)       New Process runs WScript.exe to process the VBScript file ShowParams.vbs  
             ↓ call  
            GetCommandLine()  
             ↓ parse  
            Wscript.Arguments(0) = Hello
    Wscript.Arguments(1) = Goodbye
    Wscript.Arguments(2) = Friday
     

    The parameter parsing rules are determined by WScript.exe which processes the VBScript file.
Summary:
  1. Parameters passed to ShowParams.exe are parsed by ShowParams.exe . The parameter parsing rules are determined by the C++ compiler that compiled ShowParams.exe
  2. Parameters passed to ShowParams.bat are parsed by cmd.exe which is the program that processes batch files.
  3. Parameters passed to ShowParams.vbs are parsed by WScript.exe which is the program that processes VBScript files3
(Note: If you only see the first letter of each parameter, the parameters may be in Unicode format.)


5.  The C/C++ Parameter Parsing Rules
The documented program parameter parsing rules for Microsoft C/C++ compilers may be found by searching www.msdn.com for "Parsing C++ Command-Line Arguments".
  • There is separate documentation for each version of Microsoft's C/C++ compiler.
  • Fortunately the documentation is identical for all of them.
  • Unfortunately the documentation isn't complete.
  • The implimentation changed in 2008, which isn't documented.
  • Fortunately you're reading this.


  Visual C++ Versions and Corresponding .dll

msvcr120.dll  —  Visual C++ 2013  —  (VC++ 12.0)  —  (released on October 17, 2013)
msvcr110.dl  —  Visual Studio 2012  —  (VC++ 11.0)
msvcr100.dll  —  Visual Studio 2010  —  (VC++ 10.0)
msvcr90.dll  —  Visual Studio 2008  —  (VC++ 9.0)  —  [new command line parsing]
msvcr80.dll  —  Visual Studio 2005  —  (VC++ 8.0)
msvcr71.dll  —  Visual C++ .NET 2003  —  (VC++ 7.1)
msvcr70.dll  —  Visual C++ .NET 2002  —  (VC++ 7.0)
msvcrt.dll  —  Visual C++ 6.0  —  (VC6)

Redistribution of the shared C runtime component in Visual C++



5.1  Here are the documented (and undocumented) rules:


  Note: The Rules Changed in 2008

new!
  1. Arguments are delimited by white space, which is either a space or a tab.
  2. The caret character (^) is not recognized as an escape character or delimiter. The character is handled completely by the command-line parser in the operating system before being passed to the argv array in the program.
    They are referring to the scenario discussed in sec. 6.2 below, where first the command line parser (cmd.exe) parses your command, handling such things as the escape character ^ , the redirection characters > & < , the pipe character | , the %  character which may identify environment variables that need to be expanded (e.g. %PROGRAMFILES%), etc. The rules here describe how your C/C++ executable will parse the lpCommandLine that was passed to CreateProcess() by cmd.exe or whoever calls CreateProcess().
  3. A string surrounded by double quotation marks ("string") is interpreted as a single argument, regardless of white space contained within. A quoted string can be embedded in an argument.
    The double quotes don't have to be around the whole parameter. A double quoted part may occur anywhere in the parameter.
  4. A double quotation mark preceded by a backslash (\") is interpreted as a literal double quotation mark character (").
  5. Backslashes are interpreted literally, unless they immediately precede a double quotation mark.
  6. If an even number of backslashes is followed by a double quotation mark, one backslash is placed in the argv array for every pair of backslashes, and the double quotation mark is interpreted as a string delimiter.
  7. If an odd number of backslashes is followed by a double quotation mark, one backslash is placed in the argv array for every pair of backslashes, and the double quotation mark is "escaped" by the remaining backslash, causing a literal double quotation mark (") to be placed in argv.
  8. The missing undocumented rule has to do with how doubledouble quotes ("") are handled:
    • Prior to 2008:
      If a closing " is followed immediately by another ", the 2nd " is accepted literally and added to the parameter.
    • After 2008
      1. A double quote encountered outside a double quoted block starts a double quoted block.
      2. A double quote encountered inside a double quoted block:
        • not followed by another double quote ends the double quoted block.
        • followed immediately by another double quote (e.g. ""), a single double quote is added to the output, and the double quoted block continues.

5.2  The Microsoft C/C++ Parameter Parsing Rules Rephrased
These are the rules for parsing a command line passed by CreateProcess() to a program written in C/C++:
  1. Parameters are always separated by a space or tab (multiple spaces/tabs OK)
  2. If the parameter does not contain any spaces, tabs, or double quotes, then all the characters in the parameter are accepted as is (there is no need to enclose the parameter in double quotes).
  3. Enclose spaces and tabs in a double quoted part
  4. A double quoted part can be anywhere within a parameter
  5. 2n backslashes followed by a " produce n backslashes + start/end double quoted part
  6. 2n+1 backslashes followed by a " produce n backslashes + a literal quotation mark
  7. n backslashes not followed by a quotation mark produce n backslashes
  8. undocumented rules regarding double quotes:
    Prior to 2008:
    • A " outside a double quoted block starts a double quoted block
    • A " inside a double quoted block ends the double quoted block
    • If a closing " is followed immediately by another ", the 2nd " is accepted literally and added to the parameter.
    Post 2008:
    • Outside a double quoted block a " starts a double quoted block.
    • Inside a double quoted block a " followed by a different character (not another ") ends the double quoted block.
    • Inside a double quoted block a " followed immediately by another " (i.e. "") causes a single " to be added to the output, and the double quoted block continues.

5.3  Summary of rules 5,6,7:
Use " to start/end a double quoted part
Use \" to insert a literal "
Use \\" to insert a \ then start or end a double quoted part
Use  \\\"  to insert a literal \"
Use \ to insert a literal \


5.4  Examples

Command-Line

argv[1]

Comment

CallMeIshmael

CallMeIshmael

a plain parameter can contain any characters except {space} {tab}  \  "

┌───────────────┐
"Call Me Ishmael"


Call Me Ishmael


spaces enclosed in a double quoted part

   ┌──────┐
Cal"l Me I"shmael


Call Me Ishmael


a double quoted part can be anywhere within a parameter

CallMe\"Ishmael
      ↑↑

 CallMe"Ishmael
       ↑


\" "

┌───────────────┐
"CallMe\"Ishmael"
       ↑↑


 CallMe"Ishmael
       ↑


\" "  (whether or not in a double quoted part)

┌─────────────────┐
"Call Me Ishmael\\"
                ↑↑↑


Call Me Ishmael\
    ↑          ↑


\\" \ + " (which may begin or end a double quoted part)

┌─────────────────┐
"CallMe\\\"Ishmael"
       ↑↑↑↑


 CallMe\"Ishmael
       ↑


\\\" \"     (\\ → \)  (\" → ")

a\\\b
 ↑↑↑

a\\\b
 ↑↑↑

backslashes not followed immediately by a double quotation mark are interpreted literally

"a\\\b"
  ↑↑↑

a\\\b
 ↑↑↑

whether or not the backslashes are in a double quoted part



5.5  Some Common Tasks

Command-Line

argv[1]

Comment

┌───────────────────┐
"\"Call Me Ishmael\""
 ↑↑               ↑↑


"Call Me Ishmael"
↑               ↑


the parameter includes double quotes

┌───────────┐
"C:\TEST A\\"
          ↑↑


C:\TEST A\
         ↑


the parameter includes a trailing slash

┌───────────────┐
"\"C:\TEST A\\\""
 ↑↑         ↑↑↑↑


"C:\TEST A\"
↑         ↑↑


the parameter includes double quotes and a trailing slash



5.6  The Microsoft Examples Explained

Command-Line Input

argv[1]

argv[2]

argv[3]

Comment

┌─────┐
"a b c"  d  e

┌─────┐
 a b c


d


e


spaces enclosed in double quotes

┌─────┐  ┌──┐
"ab\"c"  "\\"  d
   ↑↑     ↑↑↑

┌────┐
 ab"c
   ↑

┌─┐
 \ 
 ↑


d

\" "
\\" \ + begin or end a double quoted part

     ↓ ┌───┐ ↓
a\\\b d"e f"g h
 ↑↑↑


a\\\b

 ↑↑↑

↓     ↓
 de fg


h

backslashes not followed immediately by a double quotation mark are interpreted literally
 ↓   ↓  parameters are separated by spaces or tabs
 ┌───┐  a double quoted part can be anywhere within a parameter
the space enclosed in double quotation marks is not a delimiter

a\\\"b c d
 ↑↑↑↑

a\"b
 ↑↑

c

d

2n+1 backslashes before " → n backslashes + a literal "

     ┌───┐↓ ↓
a\\\\"b c" d e
 ↑↑↑↑↑


a\\b c


d


e

2n backslashes followed by a " produce n backslashes + start/end double quoted part.
 ↓   ↓  parameters are separated by spaces or tabs
 ┌───┐  a double quoted part can be anywhere within a parameter
the space enclosed in double quotation marks is not a delimiter



5.7  Double Double Quote Examples

(post 2008)

Command-Line Input

argv[1]

argv[2]

argv[3]

argv[4]

argv[5]

Comment

┌───────
"a b c""
      ↑↑


a b c"
     ↑

  • " Begin double quoted part.
  • "" while in a double quoted part → accept 2nd " literally, double quoted part continues

┌─────────────────┐
"""CallMeIshmael"""  b  c
 ↑↑             ↑↑


"CallMeIshmael"
↑             


b


c

  • " Begin double quoted part.
  • "" while in a double quoted part → accept 2nd " literally, double quoted part continues
  • " not followed by another " (i.e. not "") while in a double quoted part → ends the double quoted part
  • Parameters are delimited by spaces or tabs.

┌───────────────────┐
"""Call Me Ishmael"""
 ↑↑               ↑↑


"Call Me Ishmael"
↑               ↑

  • " Begin double quoted part.
  • "" while in a double quoted part → accept 2nd " literally, double quoted part continues
  • " not followed by another " (i.e. not "") while in a double quoted part → ends the double quoted part

┌──┐               ┌┐
""""Call Me Ishmael"" b c
 ↑↑


"Call


Me


Ishmael


b


c

  • " Begin double quoted part.
  • "" while in a double quoted part → accept 2nd " literally, double quoted part continues
  • " not followed by another " (i.e. not "") in a double quoted part → ends the double quoted part
  • Parameters are delimited by spaces or tabs.
  • (note "" outside of double quoted block begins and then immediately ends a double quoted part.)

(pre 2008)

Command-Line Input

argv[1]

argv[2]

argv[3]

Comment

┌─────┐
"a b c""
      ↑↑


a b c"
     ↑

"" while in a double quoted part → end double quoted part and accept 2nd " literally

┌┐              ┌┐
"""CallMeIshmael"""  b  c
 ↑↑              ↑↑


"CallMeIshmael"
↑             


b


c

" Begin double quoted part.
"" while in a double quoted part → end double quoted part and accept 2nd " literally

┌┐     ↓  ↓       ┌┐
"""Call Me Ishmael"""
 ↑↑                ↑↑


"Call


Me


Ishmael"
       ↑

Parameters are delimited by spaces or tabs.
" Begin double quoted part.
"" while in a double quoted part → end double quoted part and accept 2nd " literally

┌┐ ┌───────────────┐
""""Call Me Ishmael"" b c
 ↑↑                ↑↑


"Call Me Ishmael"
               ↑


b


c

Parameters are delimited by spaces or tabs.
" Begin double quoted part.
"" while in a double quoted part → end double quoted part and accept 2nd " literally




5.8  Triple Double Quotes

(post 2008)

How triple double quotes are parsed (post 2008)

                                     ..."""Call Me Ishmael"""...
                                        ↑↑               ↑↑↑↑
quote #1: Begin double quoted part──────┘├┘               ├┘├┘
quotes #2 & 3: Skip 1st " take 2nd " ────┘                 │
                                                          │ │
quotes 4 & 5: Skip 1st " take 2nd " ──────────────────────┘ │
quote #6: End double quoted part────────────────────────────┘


 >ShowParams.exe """Call Me Ishmael"""
 param 1 = "Call Me Ishmael"

an alternative method is

                   ┌───────────────┐
 >ShowParams.exe \""Call Me Ishmael"\"
 param 1 = "Call Me Ishmael"

or

                 ┌───────────────────┐
 >ShowParams.exe "\"Call Me Ishmael\""
 param 1 = "Call Me Ishmael"

(pre 2008)

How triple double quotes were parsed (pre 2008)

                                     ..."""Call Me Ishmael"""...
                                        ↑↑               ↑↑↑
quote #1: Begin double quoted part──────┘               │││
quote #2: End double quoted part─────────┘               │││
quote #3: and accept this " literally─────┘               │││
                                                          │││
quote #4: Begin double quoted part────────────────────────┘││
quote #5: End double quoted part───────────────────────────┘│
quote #6: and accept this " literally───────────────────────┘


 >ShowParams.exe """Call Me Ishmael"""
 param 1 = "Call
 param 2 = Me
 param 3 = Ishmael"

an alternative method is

 >ShowParams.exe \"Call Me Ishmael\"
 param 1 = "Call
 param 2 = Me
 param 3 = Ishmael"



5.9  Quadruple Double Quotes

(post 2008)

How quadruple double quotes are parsed(post 2008)

                                     ...""""Call me Ishmael""""...
                                        ↑↑↑↑              ↑↑↑↑
quote #1: Begin double quoted part──────┘├┘├┘              │├┘││
quotes #2 & 3: Skip 1st " take 2nd " ────┘                │ ││
quote #4: End double quoted part───────────┘               │ ││
                                                           │ ││
quote #5: Begin double quoted part─────────────────────────┘ ││
quotes #6 & 7: Skip 1st " take 2nd " ───────────────────────┘ ││
quote #8: End double quoted part──────────────────────────────┘
          Assuming this isn't another " ───────────────────────┘

 >ShowParams.exe """"Call Me Ishmael""""
 param 1 = "Call
 param 2 = Me
 param 3 = Ishmael"

an alternative method is

 >ShowParams.exe \"Call Me Ishmael\"
 param 1 = "Call
 param 2 = Me
 param 3 = Ishmael"

(pre 2008)

How quadruple double quotes are parsed (pre 2008)

                                     ...""""Call me Ishmael""""...
                                                       
quote #1: Begin double quoted part──────┘               │││
quote #2: End double quoted part─────────┘               │││
quote #3: and accept this " literally─────┘               │││
quote #4: Begin another double quoted part─┘               │││
                                                           │││
quote #5: End double quoted part───────────────────────────┘││
quote #6: and accept this " literally───────────────────────┘││
quote #7: Begin double quoted part───────────────────────────┘│
quote #8: End double quoted part──────────────────────────────┘
          If this was a double quote we'd accept it literally──┘

 >ShowParams.exe """"Call Me Ishmael""""
 param 1 = "Call Me Ishmael"

Note quotes #7,#8 are not necessary. They contribute nothing.

 >ShowParams.exe """"Call Me Ishmael""
 param 1 = "Call Me Ishmael"

an alternative method is

 >ShowParams.exe "\"Call Me Ishmael\""
 param 1 = "Call Me Ishmael"




5.10  The Microsoft C/C++ Command Line Parameter Parsing Algorithm

(pre 2008)

The following algorithm was reverse engineered by disassembling a small C program compiled using Microsoft Visual C++ and examining the disassembled code.

See

msvcrtparsing.htm

for the actual diasssembled code with annotations.

note if odd number of backslashes, we ignore last backslash and begin again.

1. Parse off parameter 0 (the program filename)
The entire parameter may be enclosed in double quotes (it handles double quoted parts)
(Double quotes are necessary if there are any spaces or tabs in the parameter)
There is no special processing of backslashes (\)
 
2. Parse off next parameter:
a. Skip over multiple spaces/tabs between parameters
LOOP
b. Count the backslashes (\). Let m = number of backslashes. (m may be zero.)
IF even number of backslashes (or zero)
c. IF next character following m backslashes is a double quote:
Even number of backslashes?
Yes. If we're not in a double quoted part, " begins a double quoted part.
Yes. If we're in a double quoted part, "" skip 1st take 2nd "
   If m is even (or zero)
   if currently in a double quoted part
   IF next character is also a "
   move to next character (the 2nd ". This character will be added to the parameter.)
ELSE
set flag to not add this " character to the parameter
toggle double quoted part flag (end double quoted part)
ENDIF
else
set flag to not add this " character to the parameter
endif
Endif
ENDIF
ENDIF
Add backslashes to output
m = m/2 (floor divide e.g. 0/2=0, 1/2=0, 2/2=1, 3/2=1, 4/2=2, 5/2=2, etc.)
d. add m backslashes
e. add this character to our parameter
ENDLOOP

(post 2008)

See

msvcr100dparsing.htm

for the actual diasssembled code with annotations.





6.  Who Calls CreateProcess?
So far we've talked about parsing the command line that got passed as the lpCommandLine parameter to CreateProcess(). But who calls CreateProcess? One possibility is you could write a small program which calls CreateProcess() passing it a command line.

6.1.  ProgA.exe → CreateProcess()
A small program ProgA.exe calls CreateProcess() passing it a command line:
ProgA.exe → CreateProcess()
ProgA.exe          
 ↓          
cmdline = "ShowParams.exe Hello Goodbye Friday"          
 ↓ create new process          
CreateProcess(cmdline)       New Process running ShowParams.exe  
         ↓ call  
        GetCommandLine()  
         ↓ parse  
       
argv[] = ShowParams.exe
Hello
Goodbye
Friday
 


Another possibility is you could open a Command Prompt window and type in a command:

6.2  Command Prompt Window → CreateProcess()
The command prompt window is program cmd.exe (go to START → RUN, enter "cmd"). Program cmd.exe displays the command prompt window, reads your command, and parses it handling such things as redirection characters (>) & (<), the pipe character (|), the escape character (^), identifying and expanding Environment Variables (e.g. %PROGRAMFILES%), etc. Then cmd.exe calls CreateProcess(), passing it a command line.4
Command Prompt Window (cmd.exe) → CreateProcess()
Command Prompt Window (cmd.exe)          
 ↓ get command from user          
command = "ShowParams.exe Hello Goodbye Friday < in.txt > out.txt"          
 ↓ parse and process command handling special characters like ^ < > | %
create a command line
         
cmdline = "ShowParams.exe  Hello  Goodbye  Friday"          
 ↓ create new process          
CreateProcess(cmdline)       New Process running ShowParams.exe  
           ↓ call  
          GetCommandLine()  
           ↓ parse  
         
argv[] = ShowParams.exe
Hello
Goodbye
Friday
 

Note here your command line is parsed twice:
  1. by the Command Prompt Window (cmd.exe)
  2. by ShowParams.exe
So if you want to get a parameter on the command line to ShowParams.exe, you first need to get it through the cmd.exe parser, then through the parser the Microsoft C/C++ compiler added to ShowParams.exe




7.  The cmd.exe Command Prompt Parsing Rules

The command prompt window is program cmd.exe5 (go to START → RUN, enter "cmd"). The command prompt window.

Escape Character
  • ^ is the escape character for commands passed to cmd.exe
Double Quotes:
  • An unescaped " will begin or end a double quoted part, and the double quote will be included as part of the parameter.
  • ^" outside a double quoted part will insert a " but will not start a double quoted part.
  • Environment variables are expanded both inside and outside double quotes.
Outside Double Quotes:
  • Use ^ outside double quotes to escape < > & | ^
       to insert < use ^<
       to insert > use ^>
       to insert | use ^|
       to insert ^ use ^^
       to insert " and begin a double quoted part use "
       to insert " and not begin a double quoted part use ^"
  • sometimes it may be necessary to also escape ( ) @ !
  • It's OK to escape everything

Note ^ is also the line continuation character.

Inside Double Quotes:
  • Inside Double Quotes, everything is taken literally
  • ^" gives ^" but does not end a double quoted part
  • Environment variable expansion is still performed inside double quotes
Percent Sign:
  • If your command line will be placed in a batch file, double the % character6

Note: The result is what gets passed as part of the lpCommandLine argument to the CreateProcess() API. The newly created process still needs to retrieve the lpCommandLine string and parse off the parameters using it's own parsing rules (e.g. if it's a C/C++ program, it will use the Microsoft C/C++ parameter parsing rules to create the argv[] vector. See Putting It Together below for more on this).


7.1  Examples:
Outside of Double Quotes Escape The Essential Characters < > & | ^
>ShowParams.exe !\^"#$%^&'()*+,-./0123456789:;^<=^>?@abcdefghijklmnopqrstuvwxyz[\]^^_`abcdefghijklmnopqrstuvwxyz{^|}~
                 └┴┘  ▲↑_                     ↑_ ↑_                               ↑_                             ↑_


lpCommandLine = ShowParams.exe !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
↑_  These characters have been escaped with ^
 └┴┘  The " character has been escaped with ^ so it doesn't start a double quoted part when cmd.exe parses it.
Then ^" is escaped by \ so ShowParams.exe will see it as a literal " (Microsoft C/C++ parsing rules)
The result is:
\^" → cmd.exe parses to give \" → ShowParams.exe parses to give → "
 ▲  Note if this command line is to be put in a batch file, the % will need to be doubled.



Outside of Double Quotes It's OK to Escape Everything
>ShowParams.exe ^!^\^"^#^$^%^&^'^(^)^*^+^,^-^.^/^0^1^2^3^4^5^6^7^8^9^:^;^<^=^>^?^@^A^B^C^D^E^F^G^H^I^J^K^L^M^N^O^P^Q^R 
                  └┴┴┘     ▲

  ^S^T^U^V^W^X^Y^Z^[^\^]^^^_^`^a^b^c^d^e^f^g^h^i^j^k^l^m^n^o^p^q^r^s^t^u^v^w^x^y^z^{^|^}^~


lpCommandLine = ShowParams.exe !"#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
→  line continues
└┴┴┘  The " character has been escaped with ^ so it doesn't start a double quoted part when cmd.exe parses it.
Then ^" is escaped by \ so ShowParams.exe will see it as a literal " (Microsoft C/C++ parsing rules)
itself is escaped by ^ just for the heck of it (it's OK to escape everything).
The result is:
^\^" → cmd.exe parses to give \" → ShowParams.exe parses to give → "
 ▲  Note if this command line is to be put in a batch file, the % will need to be doubled.



Inside Double Quotes Escape Nothing
                ┌─────────────────────────────────────────────────────────────────────────────────────────────┐
                │                                                                                             │

>ShowParams.exe "!#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
                  ↑ ▲


lpCommandLine = ShowParams.exe "!#$%&'()*+,-./0123456789:;<=>?@abcdefghijklmnopqrstuvwxyz[\]^_`abcdefghijklmnopqrstuvwxyz{|}~"
   "  Outside a double quoted part, when not escaped with a ^, a double quote " produces a double quote " and begins a double quoted part
   "  Inside a double quoted part, a double quote " produces a double quote " and ends the double quoted part
      The " character is always included as part of the parameter
   ↑  Note the " character has been left out of the sequence as it would end the double quoted part. (You can't escape a " while in a double quoted part. The closest we could get would be to use ^" which would give us a ^" and not end the double quoted part.)
▲  Note if this command line is to be put in a batch file, the % will need to be doubled.


7.2  Environment Variables:

Parameter

Result

Comment

PROGRAMFILES

PROGRAMFILES

 

"PROGRAMFILES"

"PROGRAMFILES"

 

%PROGRAMFILES%

C:\Program Files

an environment variable

"%PROGRAMFILES%"

"C:\Program Files"

" + an environment variable + "

%XYZ%

%XYZ%

(if XYZ is not an environment variable)






PUTTING IT TOGETHER:


8.

A command line passed to a C/C++ program passes through two parsers:

  1. The cmd.exe command line parser parses your command & parameters
  2. The C/C++ program retrieves the resulting command line and parses off the parameters

Here are some examples:

Parsing Example 1

1.  ShowParams.exe Bed Bath ^& Beyond
                            └┘
          ↓
          ↓
2.                Bed Bath & Beyond
          ↓
          ↓
          ↓
3.                Bed Bath & Beyond
          ↓         ↓    ↓  ↓   ↓
          ↓         ↓    ↓  ↓   ↓
          ↓         ↓    ↓  ↓   ↓
4.      argv[0]  argv[1] ↓  ↓   ↓
                    argv[2] ↓   ↓
                        argv[3] ↓
                             argv[4]

param 0 = ShowParams.exe

param 1 = Bed

param 2 = Bath

param 3 = &

param 4 = Beyond

1.  cmd.exe command line parser interprets ^& as an escaped & character so it does not interpret this as “start a new command.” (It's possible to put more than one DOS command on a command line by separating them with the & character.)
2.  C/C++ command line parser receives this. There are no double quotes for it to process.
3.  C/C++ command line parser parses off the command line parameters into the argv[] array.



Parsing Example 2
                        ┌─────┐
                        │     │

1.  ShowParams.exe Bed \"Bath\" ^& Beyond
                                └┘
          ↓
          ↓
2.                Bed \"Bath\" & Beyond
          ↓            └┘    └┘
          ↓
          ↓
3.                Bed "Bath" & Beyond
          ↓         ↓    ↓    ↓   ↓
          ↓         ↓    ↓    ↓   ↓
          ↓         ↓    ↓    ↓   ↓
4.      argv[0]  argv[1] ↓    ↓   ↓
                      argv[2] ↓   ↓
                          argv[3] ↓
                               argv[4]

param 0 = ShowParams.exe

param 1 = Bed

param 2 = "Bath"

param 3 = &

param 4 = Beyond

1.  cmd.exe command line parser interprets the first " as “Start a double quoted part” and the second " as “End the double quoted part.” cmd.exe does not remove the double quote characters. (Note that these double quotes are not escaped with the ^ character, which is the escape character for cmd.exe. Also note the \ character is treated as an ordinary character by cmd.exe)
cmd.exe command line parser interprets ^& as an escaped & character so it does not interpret this as “start a new command.” (It's possible to put more than one DOS command on a command line by separating them with the & character.)
2.  C/C++ command line parser receives this. The C/C++ parser interperts \" as an escaped double quote so neither one starts a double quoted part.
3.  C/C++ command line parser parses off the command line parameters into the argv[] array.



Parsing Example 3

1.  ShowParams.exe ^"Bed Bath ^& Beyond^"
                   └┘         └┘       └┘

          ↓
          ↓         ┌─────────────────┐
          ↓         │                 │

2.                 "Bed Bath & Beyond"
          ↓
          ↓                  ↓
          ↓                  ↓
          ↓
3.                  Bed Bath & Beyond
          ↓                  ↓
          ↓                  ↓
          ↓                  ↓
4.      argv[0]            argv[1]



param 0 = ShowParams.exe


param 1 = Bed Bath & Beyond

1.  cmd.exe command line parser interprets ^" as an escaped " character so it does not start or end a double quoted part.
cmd.exe command line parser interprets ^& as an escaped & character so it does not interpret this as “start a new command.” (It's possible to put more than one DOS command on a command line by separating them with the & character.)
2.  C/C++ command line parser receives this. The C/C++ parser interperts " as “start or end a double quoted part” and the double quotes are removed.
3.  C/C++ command line parser parses off the command line parameters into the argv[] array.



Parsing Example 4

1.  ShowParams.exe ^"Bed \^"Bath\^" ^& Beyond^"
                   └┘     └┘     └┘ └┘       └┘

          ↓
          ↓         ┌─────────────────────┐
          ↓         │                     │

2.                 "Bed \"Bath\" & Beyond"
          ↓              └┘    └┘
          ↓
          ↓                    ↓
          ↓                    ↓
          ↓
3.                  Bed "Bath" & Beyond
          ↓                    ↓
          ↓                    ↓
          ↓                    ↓
4.      argv[0]              argv[1]



param 0 = ShowParams.exe


param 1 = Bed "Bath" & Beyond

1.  cmd.exe command line parser interprets ^" as an escaped " character so it does not interpret this as “Start a double quoted part.”
cmd.exe command line parser interprets ^& as an escaped & character so it does not interpret this as “Start a new command.” (It's possible to put more than one DOS command on a command line by separating them with the & character.)
2.  C/C++ command line parser receives this. The C/C++ parser interperts \" as an escaped double quote so they do not start a double quoted part.
3.  C/C++ command line parser parses off the command line parameters.


It gets harder when double quotes are not escaped with ^ as cmd.exe will now interpert them as “Start or end a double quoted part.”

Parsing Example 5
                   ┌──────────────────┐
                   │                  │

1.  ShowParams.exe "Bed Bath ^& Beyond"

          ↓
          ↓        ┌──────────────────┐
          ↓        │                  │

2.                "Bed Bath ^& Beyond"
          ↓
          ↓                 ↓
          ↓                 ↓
          ↓
3.                 Bed Bath ^& Beyond
          ↓                 ↓
          ↓                 ↓
          ↓                 ↓
4.      argv[0]           argv[1]



param 0 = ShowParams.exe


param 1 = Bed Bath ^& Beyond

1.  cmd.exe command line parser interprets the first " as “Start a double quoted part” and the second " as “End the double quoted part.” cmd.exe does not remove the double quote characters.
Inside the double quoted part cmd.exe interprets all characters literally, including ^ and & 
2.  C/C++ command line parser receives this. The parser interperts the first " as “Start a double quoted part” and the second " as “End the double quoted part.” The double quotes are removed.
3.  C/C++ command line parser parses off the command line parameters into the argv[] array.
Parsing Example 6

1.  ShowParams.exe ^"Bed Bath \^" Beyond^"
                   └┘          └┘       └┘

          ↓
          ↓         ┌──────────────────┐
          ↓         │                  │

2.                 "Bed Bath \" Beyond"
          ↓
          ↓                  ↓
          ↓                  ↓
          ↓
3.                  Bed Bath " Beyond
          ↓                  ↓
          ↓                  ↓
          ↓                  ↓
4.      argv[0]            argv[1]



param 0 = ShowParams.exe


param 1 = Bed Bath " Beyond

1.  cmd.exe command line parser interprets ^" as an escaped " character so it does not start or end a double quoted part.
cmd.exe command line parser interprets ^& as an escaped & character so it does not interpret this as “start a new command.” (It's possible to put more than one DOS command on a command line by separating them with the & character.)
2.  C/C++ command line parser receives this. The parser interperts " as “start or end a double quoted part” and the double quotes are removed.
3.  C/C++ command line parser parses off the command line parameters into the argv[] array.



Parsing Example 7

1.  ShowParams.exe ^"Bed Bath \\\^" Beyond^"
                   └┘            └┘       └┘

          ↓
          ↓         ┌────────────────────┐
          ↓         │                    │

2.                 "Bed Bath \\\" Beyond"
                              └┴┴┘
          ↓
          ↓                  ↓
          ↓                  ↓
          ↓
3.                  Bed Bath \" Beyond
          ↓                  ↓
          ↓                  ↓
          ↓                  ↓
4.      argv[0]            argv[1]



param 0 = ShowParams.exe


param 1 = Bed Bath \" Beyond

1.  cmd.exe command line parser interprets ^" as an escaped " character so it does not start or end a double quoted part. The ^ is removed. The " is not removed.
2.  The C/C++ command line parser receives this.
The C/C++ command line parser interperts the first
 " as “Start a double quoted part” and the double quote is removed.
The C/C++ command line parser interperts
 \\\" inside the double quoted part as a literal \" 
The C/C++ command line parser interperts the ending
 " as “End the double quoted part” and the double quote is removed.
3.  The C/C++ command line parser fills the argv[] array with the result.



Parsing Example 8

1.  ShowParams.exe ^"\^"Bed Bath Beyond\^"^"
                   └┘ └┘                └┘└┘

          ↓
          ↓          ┌───────────────────┐
          ↓          │                   │

2.                  "\"Bed Bath Beyond\""
                      └┘               └┘
          ↓
          ↓                  ↓
          ↓                  ↓
          ↓
3.                    "Bed Bath Beyond"
          ↓                  ↓
          ↓                  ↓
          ↓                  ↓
4.      argv[0]            argv[1]



param 0 = ShowParams.exe


param 1 = "Bed Bath Beyond"

1.  cmd.exe command line parser interprets ^" as an escaped " character so it does not start or end a double quoted part. The ^ is removed. The " is not removed.
cmd.exe command line parser interprets
 \ as an ordinary character.
cmd.exe command line parser interprets
 ^" as an escaped " character so it does not start or end a double quoted part. The ^ is removed. The " is not removed.
cmd.exe command line parser interprets
 Bed Bath Beyond\ as ordinary characters.
cmd.exe command line parser interprets
 ^" as an escaped " character so it does not start or end a double quoted part. The ^ is removed. The " is not removed.
cmd.exe command line parser interprets
 ^" as an escaped " character so it does not start or end a double quoted part. The ^ is removed. The " is not removed.
2.  The C/C++ command line parser receives this.
The C/C++ command line parser interperts the first
 " as “Start a double quoted part” and the double quote is removed.
The C/C++ command line parser interperts
 \" inside the double quoted part as a literal " 
The C/C++ command line parser interperts
 \" inside the double quoted part as a literal " 
The C/C++ command line parser interperts the ending
 " as “End the double quoted part” and the double quote is removed.
3.  The C/C++ command line parser fills the argv[] array with the result.





8.1

How To Pass A Parameter to:
a C/C++ Program from the Command Line

A Simplified method: (new!2016)

The trick is to enclose parameters with ^" instead of just "

e.g. instead of:

"Bed Bath Beyond"

use

^"Bed Bath Beyond^"

The command processor cmd.exe will strip off the ^ and won't start any double quoted parts.

Simplified method steps:

  1. Start with the parameter you want

  2. Escape with ^ all these special characters: ^ < > | & ( ) "

    i.e. replace  ^  with  ^^
         replace  <  with  ^<
         replace  >  with  ^>
         replace  |  with  ^|
         replace  &  with  ^&
         replace  (  with  ^(
         replace  )  with  ^)
         replace  "  with  ^"


  3. Then, for all ^", double the number of \ immediately preceeding the ^" (there may be zero, in which case do nothing).

  4. And for all ^", add a preceeding \

    Steps 3 & 4 combined are:
    replace  ^"  with  \^"
         replace  \^"  with  \\\^"
         replace  \\^"  with  \\\\\^"
         replace  \\\^"  with  \\\\\\\^"
         etc.

  5. Add a leading and trailing ^"

Example 8.1: Command Line to C/C++ Program

Example 8.1:     Bed Bath & Beyond

Parameter

1. Start with the parameter you want (parameter includes spaces)

  Bed Bath & Beyond

2. Escape with ^ all special characters: ^ < > | & ( ) "
i.e. replace  ^  with  ^^
     replace  <  with  ^<
     replace  >  with  ^>
     replace  |  with  ^|
     replace  &  with  ^&
     replace  (  with  ^(
     replace  )  with  ^)
     replace  "  with  ^"

  Bed Bath ^& Beyond
           ↑

3. For all ^", double the number of \ immediately preceeding the ^"
(there may be zero)

no \^" in parameter

4. For all ^", add a leading \

no ^" in parameter

5. Add a leading and trailing ^"

^"Bed Bath ^& Beyond^"
↑↑                  ↑↑

6. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

^"Bed Bath ^& Beyond^"

 
Now examine how this parameter gets parsed:
A. cmd.exe treats ^ as escape character and removes them.
cmd.exe finds no double quoted parts because all " are escaped with ^

^"Bed Bath & Beyond^"
↓                  ↓

B. cmd.exe passes this as parameter to the C/C++ program

 "Bed Bath & Beyond"

C. C/C++ program sees this as a double quoted parameter.

 ┌─────────────────┐
 "Bed Bath & Beyond"
 ↓                 ↓

D. C/C++ program applies the rule 2n+1 backslashes followed by a "
produce n backslashes + a literal "

no \" in parameter

E. Result is the desired parameter we started with

  Bed Bath & Beyond

Sample Test File:

Example 8.2:     Bed "Bath" & Beyond

Parameter

1. Start with the parameter you want (parameter includes spaces)

  Bed "Bath" & Beyond

2. Escape with ^ all special characters: ^ < > | & ( ) "
i.e. replace  ^  with  ^^
     replace  <  with  ^<
     replace  >  with  ^>
     replace  |  with  ^|
     replace  &  with  ^&
     replace  (  with  ^(
     replace  )  with  ^)
     replace  "  with  ^"

  Bed ^"Bath^" ^& Beyond
      ↑_    ↑_ ↑_

3. For all ^", double the number of \ immediately preceeding the ^"
(there may be zero)

no \^" in parameter

4. For all ^", add a leading \

  Bed \^"Bath\^" ^& Beyond
      ↑__    ↑__

5. Add a leading and trailing ^"

^"Bed \^"Bath\^" ^& Beyond^"
↑↑                        ↑↑

6. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

^"Bed \^"Bath\^" ^& Beyond^"

 
Now examine how this parameter gets parsed:
A. cmd.exe treats ^ as escape character and removes them.
cmd.exe finds no double quoted parts because all " are escaped with ^

^"Bed \^"Bath\^" ^& Beyond^"
↓      ↓      ↓  ↓        ↓ 

B. cmd.exe passes this as parameter to the C/C++ program

 "Bed \"Bath\" & Beyond"

C. C/C++ program sees this as a double quoted parameter.
Note that all " inside the parameter are escaped with \

 ┌─────────────────────┐
 "Bed \"Bath\" & Beyond"
 ↓                     ↓

D. C/C++ program applies the rule 2n+1 backslashes followed by a "
produce n backslashes + a literal quotation mark.
\" becomes " (for n=0)

  Bed \"Bath\" & Beyond
      ↓     ↓

E. Result is the desired parameter we started with

  Bed "Bath" & Beyond

Sample Test File:

Example 8.3:     Bed Bath & \"Beyond"

Parameter

1. Start with the parameter you want (parameter includes spaces)

  Bed Bath & \"Beyond"

2. Escape with ^ all special characters: ^ < > | & ( ) "
i.e. replace  ^  with  ^^
     replace  <  with  ^<
     replace  >  with  ^>
     replace  |  with  ^|
     replace  &  with  ^&
     replace  (  with  ^(
     replace  )  with  ^)
     replace  "  with  ^"

  Bed Bath ^& \^"Beyond^"
           ↑   ↑       ↑ 

3. For all ^", double the number of \ immediately preceeding the ^"
(there may be zero)

  Bed Bath ^& \\^"Beyond^"
              ↑

4. For all ^", add a leading \

  Bed Bath ^& \\\^"Beyond\^"
                ↑__      ↑__

5. Add a leading and trailing ^"

^"Bed Bath ^& \\\^"Beyond\^"^"
↑↑                          ↑↑

6. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

^"Bed Bath ^& \\\^"Beyond\^"^"

 
Now examine how this parameter gets parsed:
A. cmd.exe treats ^ as escape character and removes them.
cmd.exe finds no double quoted parts because all " are escaped with ^

^"Bed Bath ^& \\\^"Beyond\^"^"
↓          ↓     ↓        ↓ ↓

B. cmd.exe passes this as parameter to the C/C++ program

 "Bed Bath & \\\"Beyond\""

C. C/C++ program sees this as a double quoted parameter.
Note that all " inside the parameter are escaped with \

 ┌───────────────────────┐
 "Bed Bath & \\\"Beyond\""
 ↓                       ↓

D. C/C++ program applies the rule 2n+1 backslashes followed by a "
produce n backslashes + a literal quotation mark
\" becomes "
\\\" becomes \"

  Bed Bath & \\\"Beyond\"
             ----      --

E. Result is the desired parameter we started with

  Bed Bath & \"Beyond"

Sample Test File:

Example 8.4:     (Bed) <Bath> & "Beyond" | \"Kohl^s

Parameter

1. Start with the parameter you want (parameter includes spaces)

  (Bed) <Bath> & "Beyond" | \"Kohl^s

2. Escape with ^ all special characters: ^ < > | & ( ) "
i.e. replace  ^  with  ^^
     replace  <  with  ^<
     replace  >  with  ^>
     replace  |  with  ^|
     replace  &  with  ^&
     replace  (  with  ^(
     replace  )  with  ^)
     replace  "  with  ^"

  ^(Bed^) ^<Bath^> ^& ^"Beyond^" ^| \^"Kohl^^s
  ↑    ↑  ↑     ↑  ↑  ↑       ↑  ↑   ↑     ↑

3. For all ^", double the number of \ immediately preceeding the ^"
(there may be zero)

  ^(Bed^) ^<Bath^> ^& ^"Beyond^" ^| \\^"Kohl^^s
                                    ↑

4. For all ^", add a leading \

  ^(Bed^) ^<Bath^> ^& \^"Beyond\^" ^| \\\^"Kohl^^s
                      ↑        ↑        ↑

5. Add a leading and trailing ^"

^"^(Bed^) ^<Bath^> ^& \^"Beyond\^" ^| \\\^"Kohl^^s^"
↑↑                                                ↑↑

6. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

^"^(Bed^) ^<Bath^> ^& \^"Beyond\^" ^| \\\^"Kohl^^s^"

 
Now examine how this parameter gets parsed:
A. cmd.exe treats ^ as escape character and removes them.
cmd.exe finds no double quoted parts because all " are escaped with ^

^"^(Bed^) ^<Bath^> ^& \^"Beyond\^" ^| \\\^"Kohl^^s^"
↓ ↓    ↓  ↓     ↓  ↓   ↓        ↓  ↓     ↓     ↓  ↓

B. cmd.exe passes this as parameter to the C/C++ program

  "(Bed) <Bath> & \"Beyond\" | \\\"Kohl^s"

C. C/C++ program sees this as a double quoted parameter.
Note all " inside the double quoted parameter are escaped with \

  ┌──────────────────────────────────────┐
  "(Bed) <Bath> & \"Beyond\" | \\\"Kohl^s"
  ↓                                      ↓

D. C/C++ program applies the rule 2n+1 backslashes followed by a "
produce n backslashes + a literal quotation mark
\" becomes "
\\\" becomes \"

   (Bed) <Bath> & \"Beyond\" | \\\"Kohl^s
                  --      --   ----

E. Result is the desired parameter we started with

   (Bed) <Bath> & "Beyond" | \"Kohl^s

Sample Test File:

Example 8.5:     &<>^|()@!"

Parameter

Start with the parameter you want
(parameter includes a space)

  &<>^|()@!"

1. Escape with ^ all special characters: ^ < >| & ( ) "
i.e. replace  ^  with  ^^
     replace  <  with  ^<
     replace  >  with  ^>
     replace  |  with  ^|
     replace  &  with  ^&
     replace  (  with  ^(
     replace  )  with  ^)
     replace  "  with  ^"

  ^&^<^>^^^|^(^)^@^!^"
  ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑

2. For all ^", double the number of \ immediately preceeding the ^"
(there may be zero)

no \^" in parameter

3. For all ^", add a leading \

  ^&^<^>^^^|^(^)^@^!\^"
                    ↑

4. Add a leading and trailing ^"

^"^&^<^>^^^|^(^)^@^!\^"^"
↑↑                     ↑↑

5. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

^"^&^<^>^^^|^(^)^@^!\^"^"

 
Now examine how this parameter gets parsed:
A. cmd.exe treats ^ as escape character and removes them.
cmd.exe finds no double quoted parts because all " are escaped with ^

^"^&^<^>^^^|^(^)^@^!\^"^"
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓  ↓ ↓

B. cmd.exe passes this as parameter to the C/C++ program

 "&<>^|()@!\""

C. C/C++ program sees this as a double quoted parameter.
Note all " inside the double quoted parameter are escaped with \

 ┌───────────┐
 "&<>^|()@!\""
 ↓           ↓

D. C/C++ program applies the rule 2n+1 backslashes followed by a "
produce n backslashes + a literal quotation mark
\" becomes "

  &<>^|()@!\"
           --

E. Result is the desired parameter we started with

  &<>^|()@!"

Sample Test File:


The following older examples show the harder way of figuring out what to use as parameters.


Example 8.6: Command Line to C/C++ Program

Example 8.6a:     &<>^|()@ !

Parameter

Start with the parameter you want
(parameter includes a space)

&<>^|()@ !

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

nothing to replace

b. enclose the whole parameter in double quotes
(because there's a space in the parameter)

┌──────────┐
"&<>^|()@ !"
↑          ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

┌──────────┐
"&<>^|()@ !"

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

Nothing to escape because it's all in a double quoted part
(as seen by cmd.exe)

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

"&<>^|()@ !"

Example 8.6b:     &<>^|()@ !

Parameter

Start with the parameter you want
(same as example 1a)

&<>^|()@ !

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

nothing to replace

b. enclose spaces in double quotes
(A double quoted part can be anywhere within a parameter)

&<>^|()@" "!
        ↑ ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

        ┌─┐
&<>^|()@" "!

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

                ┌─┐
^&^<^>^^^|^(^)^@" "^!
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑    ↑ 

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

^&^<^>^^^|^(^)^@" "^!

Sample Test File:

Example 8.7: Command Line to C/C++ Program

Example 8.7:     &<>^|@()!"&<>^|@() !

Parameter

Start with the parameter you want

&<>^|@()!"&<>^|@()!

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

&<>^|@()!\"&<>^|@()!
         ↑

b. enclose the whole parameter in double quotes

"&<>^|@()!\"&<>^|@()!"
↑                    ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

┌──────────┐         ┌───
"&<>^|()@!\"&<>^|@()!"

we have a problem in that the final " is interpreted by cmd.exe as opening a double quoted part. To avoid this, escape that last " ( the escape character for cmd.exe is ^ )

┌──────────┐
"&<>^|()@!\"&<>^|@()!^"
                     ↑

b. escape the other special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

┌──────────┐
"&<>^|@()!\"^&^<^>^^^|@()!^"
            ↑ ↑ ↑ ↑ ↑

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

"&<>^|@()!\"^&^<^>^^^|@()!^"


Another way to get the same result would be at step 1b don't enclose the parameter in double quotes, then escape all special characters including the double quote so it doesn't start a double quoted part.

^&^<^>^^^|@()!\^"^&^<^>^^^|@()!
↑ ↑ ↑ ↑ ↑      ↑ ↑ ↑ ↑ ↑ ↑

Sample Test File:

Example 8.8: Command Line to C/C++ Program

Example 8.8a:     &<>^|@() !"&<>^|@() !

Parameter

Start with the parameter you want (parameter includes leading and trailing double quotes, plus a double quote inside, and two spaces)

"&<>^|@() !"&<>^|@() !"

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

\"&<>^|@() !\"&<>^|@() !\"
↑           ↑           ↑

b. enclose the whole parameter in double quotes

┌──────────────────────────┐
"\"&<>^|@() !\"&<>^|@() !\""
↑                          ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

┌─┐           ┌───────────┐┌───
"\"&<>^|@() !\"&<>^|@() !\""

we have a problem in that the final " is interpreted by cmd.exe as opening a double quoted part. To avoid this, escape that last " ( the escape character for cmd.exe is ^ )

┌─┐           ┌───────────┐
"\"&<>^|@() !\"&<>^|@() !\"^"
                           ↑

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

┌─┐                ┌───────────┐
"\"^&^<^>^^^|@() !\"&<>^|@() !\"^"
   ↑ ↑ ↑ ↑ ↑

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

"\"^&^<^>^^^|@() !\"&<>^|@() !\"^"


Another way to get the same result would be at step 2a just escape all special characters including all double quotes so there are no double quoted parts.

^"\^"^&^<^>^^^|@() !\^"^&^<^>^^^|@() !\^"^"
↑  ↑ ↑ ↑ ↑ ↑ ↑       ↑ ↑ ↑ ↑ ↑ ↑       ↑ ↑

Example 8.8b:     &<>^|@() !"&<>^|@() !

Parameter

Start with the parameter you want (same as 3a)

"&<>^|@() !"&<>^|@() !"

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

\"&<>^|@() !\"&<>^|@() !\"
↑           ↑           ↑

b. enclose spaces in double quotes
(A double quoted part can be anywhere within a parameter)

          ┌─┐           ┌─┐
\"&<>^|@()" "!\"&<>^|@()" "!\"
          ↑ ↑           ↑ ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

 ┌────────┐ ┌──┐        ┌─┐  ┌───
\"&<>^|@()" "!\"&<>^|@()" "!\"

once again we have a problem in that the final " is interpreted by cmd.exe as opening a double quoted part. To avoid this, escape that last " ( the escape character for cmd.exe is ^ )

 ┌────────┐ ┌──┐        ┌─┐
\"&<>^|@()" "!\"&<>^|@()" "!\^"
                             ↑

b. escape the other special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

 ┌────────┐ ┌──┐             ┌─┐
\"&<>^|@()" "!\"^&^<^>^^^|@()" "!\^"
                ↑ ↑ ↑ ↑ ↑

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

\"&<>^|@()" "!\"^&^<^>^^^|@()" "!\^"


Another way to get the same result would be at step 2a just escape all special characters including all double quotes so there are no double quoted parts.

\^"^&^<^>^^^|@()^" ^"!\^"^&^<^>^^^|@()^" ^"!\^"
 ↑ ↑ ↑ ↑ ↑ ↑    ↑  ↑   ↑ ↑ ↑ ↑ ↑ ↑    ↑  ↑   ↑

Sample Test File:

Example 8.9: Command Line to C/C++ Program

Example 8.9a:     "C:\TEST A\"

Parameter

Start with the parameter you want (parameter includes double quotes and a space)

"C:\TEST A\"

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

\"C:\TEST A\\\"
↑          ↑↑

b. enclose the whole parameter in double quotes
(because there's a space in the parameter)

┌───────────────┐
"\"C:\TEST A\\\""
↑               ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

┌─┐            ┌┐
"\"C:\TEST A\\\""

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

No special characters to escape
(as seen by cmd.exe)

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

"\"C:\TEST A\\\""


Another way to get the same result would be at step 2a just escape all special characters including all double quotes so there are no double quoted parts.

^"\^"C:\TEST A\\\^"^"
↑  ↑             ↑ ↑

Example 8.9b:     "C:\TEST A\"

Parameter

Start with the parameter you want (same as 4a)

"C:\TEST A\"

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

\"C:\TEST A\\\"
↑          ↑↑

b. enclose spaces in double quotes
(A double quoted part can be anywhere within a parameter)

\"C:\TEST" "A\\\"
         ↑ ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

 ┌───────┐ ┌────┐ \"C:\TEST" "A\\\"

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

It's all in double quoted parts.
(as seen by cmd.exe)

c. if this will be placed in a batch file, double the % characters

no % in parameter

Result: To get desired parameter use this:

\"C:\TEST" "A\\\"


Another way to get the same result would be at step 2a just escape all special characters including all double quotes so there are no double quoted parts.

\^"C:\TEST^" ^"A\\\^"
 ↑        ↑  ↑     ↑

Sample Test File:

Example 8.10: Command Line to C/C++ Program

Example 8.10a:     "C:\TEST %&^ A\"

Parameter

Start with the parameter you want

"C:\TEST %&^ A\"

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

\"C:\TEST %&^ A\\\"
↑              ↑↑

b. enclose the whole parameter in double quotes

┌───────────────────┐
"\"C:\TEST %&^ A\\\""

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

┌─┐                ┌┐
"\"C:\TEST %&^ A\\\""

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

┌─┐                   ┌┐
"\"C:\TEST ^%^&^^ A\\\""
           ↑ ↑ ↑

c. if this will be placed in a batch file, double the % characters

┌─┐                    ┌┐
"\"C:\TEST ^%%^&^^ A\\\""
            ↑

Result: To get desired parameter use this:

"\"C:\TEST ^%%^&^^ A\\\""


Another way to get the same result would be at step 2a just escape all special characters including all double quotes so there are no double quoted parts.

^"\^"C:\TEST %^&^^ A\\\^"^"
↑  ↑          ↑ ↑      ↑ ↑

c. and if this will be placed in a batch file, double the % characters

^"\^"C:\TEST %%^&^^ A\\\^"^"
             ↑

Example 8.10b:     "C:\TEST %&^ A\"

Parameter

Start with the parameter you want (same as 5a)

"C:\TEST %&^ A\"

1. Apply the Microsoft C/C++ parsing rules
a.
replace: literal  "  with   \"
literal \"  with  \\\"
literal \\" with \\\\\"

\"C:\TEST %&^ A\\\"
↑              ↑↑

b. enclose spaces in double quotes
(A double quoted part can be anywhere within a parameter)

         ┌─┐   ┌─┐
\"C:\TEST" "%&^" "A\\\"
         ↑ ↑   ↑ ↑

2. Apply the Command Prompt parsing rules (cmd.exe)
a. determine what cmd.exe will see as the quoted parts

 ┌───────┐ ┌───┐ ┌────┐
\"C:\TEST" "%&^" "A\\\"

b. escape the special characters not in double quoted parts:
( the escape character for cmd.exe is ^ )

it's all in double quoted parts
(as seen by cmd.exe)

c. if this will be placed in a batch file, double the % characters

\"C:\TEST" "%%&^" "A\\\"
            ↑

Result: To get desired parameter use this:

"\"C:\TEST ^%%^&^^ A\\\""


Another way to get the same result would be at step 2a just escape all special characters including all double quotes so there are no double quoted parts.

\^"C:\TEST^" ^"%^&^^^" ^"A\\\^"
 ↑        ↑  ↑  ↑ ↑ ↑   ↑    ↑

c. and if this will be placed in a batch file, double the % characters

\^"C:\TEST^" ^"%%^&^^^" ^"A\\\^"
               ↑


Sample Test File:
  • TestParams8_10.bat
    • REM all 4 lines should produce the same result
      ShowParams.exe "\"C:\TEST ^%%^&^^ A\\\""
      ShowParams.exe ^"\^"C:\TEST %%^&^^ A\\\^"^"
      ShowParams.exe "\"C:\TEST ^%%^&^^ A\\\""
      ShowParams.exe \^"C:\TEST^" ^"%%^&^^^" ^"A\\\^"





8.2

How To Pass A Parameter to:
a Batch File from the Command Line

To get a parameter into a batch file you need to work backwards through the two parsings it will go through:

  1. The cmd.exe command line parser parses your command line & parameters and then starts running the batch file
  2. The batch file retrieves the parameters, but when you use them, they get parsed again. Here's an example:
    1. a batch file line contains %1
    2. %1 is replaced with your parameter
    3. that line is then sent to the command line parser for execution, where it gets parsed
Overview of steps:
  1. Start with the parameter you want
  2. Apply the command line parsing rules that cmd.exe will apply when you enter the command and parameters  [sec. 7]
  3. Again, apply the command line parsing rules that cmd.exe will apply when you enter the command and parameters  [sec. 7]
Step Details:
The steps to create your parameter are:
  1. Apply the Command Line Parser (cmd.exe) parsing rules
    1. determine what cmd.exe will see as the quoted parts
    2. escape the special characters < > | & ^ not in a double quoted part
    3. again, escape the special characters < > | & ^ not in a double quoted part
      • •the escape character for cmd.exe is ^
      • •it may also be necessary to escape ( ) @ !
      • •it's OK to escape everything
    4. If your command line will be placed in a batch file, double the % character7

The Command Line to Batch File Rules:

Combining the two steps above gives us the following rules:

  • Use ^^^ outside double quotes to escape < > & | ^
       to insert < use ^^^<
       to insert > use ^^^>
       to insert | use ^^^|
       to insert ^ use ^^^^
  • Sometimes it may be necessary to also escape ( ) @ !
  • It's OK to escape everything
  • Note ^ is also the line continuation character.

Space, Tab, Semicolon, Comma:
Note that you can't have a space within a parameter without having some double quotes around it somehow. All spaces must be within a double quoted part. Otherwise the space will act as a parameter delimiter. (Same with tabs, semicolons, commas.)
  1. Command line arguments can be separated by an unquoted semicolon or comma. This affects builtin commands like COPY and batch file parameters.
  2. If the command is called by name (no path), the following slash is treated a separator, as in DIR/P. This affects both builtin commands and external commands. Note: If you want to use NT-style slash-separated paths with DOS utilities you have to quote them (as in TYPE "C:/BOOT.INI").16

Example 9.1: Command Line to Batch File

Example 9.1:     &<>^|()@! Parameter

Start with the parameter you want

&<>^|()@!

1. Apply the Command Line to Batch File parsing rules
a. determine what cmd.exe will see as the quoted parts:

it's all unquoted

b. escape the special characters not in double quoted parts:

^^^&^^^<^^^>^^^^^^^|()@!
↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑

Result: To get desired parameter use this:

^^^&^^^<^^^>^^^^^^^|()@!

Sample Test File:
  • TestParams9_1.bat
    • ShowParams.bat ^^^&^^^<^^^>^^^^^^^|()@!

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)

Example 9.2: Command Line to Batch File

Example 9.2:     &<>^|()@!

Parameter

Start with the parameter you want

┌─────────┐
"&<>^|()@!"

1. Apply the Command Line to Batch File parsing rules
a. determine what cmd.exe will see as the quoted parts:

it's all in a double quoted part

b. escape the special characters not in double quoted parts:

Nothing to escape because it's all in a double quoted part
(as seen by cmd.exe)

Result: To get desired parameter use this:

"&<>^|()@!"

Sample Test File:
  • TestParams9_2.bat
    • ShowParams.bat "&<>^|()@!"

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)

Example 9.3: Command Line to Batch File

Example 9.3:     &<>^|()@!"&<>^|()@!

Parameter

Start with the parameter you want

&<>^|()@!"&<>^|()@!

1. Apply the Command Line to Batch File parsing rules
a. determine what cmd.exe will see as the quoted parts:

┌─────────┐         ┌───
"&<>^|()@!"&<>^|()@!"

we have a problem in that the final " is interpreted by cmd.exe as opening a double quoted part. To avoid this, escape that last " 

┌─────────┐
"&<>^|()@!"&<>^|()@!^^^"
                    ↑↑↑

b. escape the special characters not in double quoted parts:

┌─────────┐
"&<>^|()@!"^^^&^^^<^^^>^^^^^^^|()@!^^^"
           ↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑

Result: To get desired parameter use this:

"&<>^|()@!"^^^&^^^<^^^>^^^^^^^|()@!^^^"

Though not necessary, it's OK to escape the rest of the characters:

┌─────────┐
"&<>^|()@!"^^^&^^^<^^^>^^^^^^^|^^^(^^^)^^^@^^^!^^^"
                               ↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑

An easier way to get the same result would be to escape the " within the parameter so it doesn't end the double quoted part. Then nothing else needs to be escaped since it's all in a double quoted part.

┌────────────────────┐
"&<>^|()@!\"&<>^|()@!"
          ↑

Another way to get the same result would be to escape all the special characters including all the " so nothing is in a double quoted part:

^^^"^^^&^^^<^^^>^^^^^^^|()@!^^^"^^^&^^^<^^^>^^^^^^^|()@!^^^"

As before, though it's not necessary, it is OK to escape the rest of the characters:

^^^"^^^&^^^<^^^>^^^^^^^|^^^(^^^)^^^@^^^!^^^"^^^&^^^<^^^>^^^^^^^|^^^(^^^)^^^@^^^!^^^"
                        ↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑                         ↑↑↑ ↑↑↑ ↑↑↑ ↑↑↑

Sample Test Files:

all 5 test files should produce the same result

  • TestParams9_3a.bat
    • ShowParams.bat "&<>^|()@!"^^^&^^^<^^^>^^^^^^^|()@!^^^"

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)

  • TestParams9_3b.bat
    • ShowParams.bat "&<>^|()@!"^^^&^^^<^^^>^^^^^^^|^^^(^^^)^^^@^^^!^^^"

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)

  • TestParams9_3c.bat
    • ShowParams.bat "&<>^|()@!\"&<>^|()@!"

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)

  • TestParams9_3d.bat
    • ShowParams.bat ^^^"^^^&^^^<^^^>^^^^^^^|()@!^^^"^^^&^^^<^^^>^^^^^^^|()@!^^^"

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)

  • TestParams9_3e.bat
    • ShowParams.bat ^^^"^^^&^^^<^^^>^^^^^^^|^^^(^^^)^^^@^^^!^^^"^^^&^^^<^^^>^^^^^^^|^^^(^^^)^^^@^^^!^^^"

      (Note: Since this is a batch file, if there were any % we would need to double them. WARNING! When executing a batch file from inside another batch file, the original batch file is terminated before the other one starts. This method of invoking a batch file from another is usually referred to as chaining.)




10.

How To Pass A Parameter to:
a VBScript, JScript, or WSH Script
from the Command Line
VBScript (.vbs), JScript9 (.js), and WSH (.wsh) scripts are run by program wscript.exe, which is the Microsoft® Windows® Script Host (WSH). WSH is a language-independent scripting host for Windows Script compatible scripting engines. Microsoft provides both Microsoft Visual Basic Script and JScript scripting engines with WSH. Windows Script Host executes scripts that exist outside an HTML or ASP page and that stand on their own as text files.10
 Example: > C:\WINDOWS\system32\wscript.exe ShowParams.vbs hello goodbye Friday 
When you run ShowParams.vbs you'll notice the window title says, "Window Script Host".

“If you want to get picky, the truth is that you can’t read command-line arguments using VBScript; that’s because VBScript doesn’t know anything about command-line arguments. But that’s all right; after all, VBScript doesn’t have to know anything about command-line arguments. That’s because Windows Script Host takes care of all that stuff.

“Any time you supply a command-line argument to a script that runs under Windows Script Host (that includes JScript scripts as well as VBScript scripts) those arguments are automatically stored in the Wscript.Arguments collection.”

     —Hey, Scripting Guy! 2008 Winter Scripting Games: Retrieving Command-Line Arguments
          http://www.microsoft.com/technet/scriptcenter/funzone/games/tips08/gtip0104.mspx

drscripto


On startup wscript.exe calls GetCommandLine() to get the command line, then calls wscript!SplitCommandLine(), which parses off the parameters. The rules for WSH command line parameter parsing are simple:

10.1  The WSH Command Line Parameter Parsing Rules:

  • parameters are separated by a space or tab (multiple spaces/tabs OK)
  • " begins and ends the double quoted part)
  • all characters within a double quoted part are accepted, including spaces and tabs)
  • The " character itself is always stripped from the parameter)
    (Note this means you can not pass a double quote as part of a parameter.))

10.2  The Microsoft® Windows® Script Host (WSH) Command Line Parameter Parsing Algorithm:

The following algorithm was reverse engineered by disassembling and examining wscript.exe and cscript.exe:

Algorithm:

  1. call Kernel32.dll!GetCommandLine() to get the command line
  2. make a copy of the command line
  3. call wscript!SplitCommandLine() dry run to determine the number of parameters
  4. allocate space for argv[] array
  5. call wscript!SplitCommandLine() to parse parameters and fill in argv[] array
wscript!SplitCommandLine() builds an argv[] array of where each parameter begins, and writes a terminating NULL at the end of each parameter.

Splitting the Command Line to get Parameters

start with: ShowParams.exe hello goodbye Friday
end with:   ShowParams.exe0hello0goodbye0Friday0
            ↑              ↑     ↑       
 argv[]     │              │     │       
param 0: ───┘              │     │       
param 1: ──────────────────┘     │       
param 2: ────────────────────────┘       
param 3: ────────────────────────────────┘

 loop parse off next parameter: skip over spaces, tabs clear " flag save starting address of this parameter LOOP process this character: If space or tab if " flag set accept this space or tab as part of the parameter else write a 0 here to terminate this parameter and goto parse off next parameter Else if " toggle " flag, strip " (shift rest of line left 1 char) move to next char ENDLOOP endloop 
Notes:
  • " flag - set if we're currently inside a double quoted part
  • The algorithm only looks for {space}, {tab}, and ("). All other characters are just characters.
  • You can't pass a double quote (") as part of a parameter because double quotes are always stripped off.

10.3  Putting it together:
When you launch a VBScript, JScript, or WSH script from the command line, your command line goes through two parsers:
  1. First the cmd.exe command line parser parses your command & parameters, using its cmd.exe parsing rules. It builds a command line, and calls CreateProcess() passing it the command line it built.
  2. Then the Windows Script Host retrieves that resulting command line and parses off the parameters, using its WSH parsing rules.
So to get a parameter into a VBScript, JScript, or WSH script, you need to work backwards through the two parsers it will go through:
  1. Start with the parameter you want
  2. Apply the WSH parsing rules that Windows Script Host will apply when parsing the command line it retrieves
  3. Then apply the command line parsing rules that cmd.exe will apply when you enter the command and parameters
Step Details:
The steps to create your parameter are:
  1. Apply the WSH parsing rules:
    1. if there are any spaces or tabs within a parameter, add some " so they are enclosed in a double quoted part
      •                                                         ┌───────────────┐
        •you may enclose the entire parameter in double quotes: "Call Me Ishmael"
      •                                                           ┌─┐  ┌─┐
        •or you may enclose just the spaces in double quotes: Call" "Me" "Ishmael
      •                                  ┌────────┐
        •or you can make a mess of it: Ca"ll Me Is"hmael

  2. Apply the Command Line Parser (cmd.exe) parsing rules
    1. determine what cmd.exe will see as the double quoted parts
    2. escape the special characters < > | & ^ not in a double quoted part
      • •the escape character for cmd.exe is ^
      • •it may also be necessary to escape ( ) @ !
      • •it's OK to escape everything
    3. If your command line will be placed in a batch file, double the % character11

10.4  Sample Scripts:


Sample 'Show Parameters' scripts:

VBScript:
  • ShowParams.vbs
    • If Wscript.Arguments.Count = 0 Then
              Wscript.echo "No parameters found"
      Else
          i=0
              Do until i = Wscript.Arguments.Count
              Parms = Parms & "Param " & i & " = " & Wscript.Arguments(i) & " " & vbcr
              i = i+1
              loop
              Wscript.echo parms
      End If
JScript:
  • ShowParams.js
    • if (WScript.Arguments.Count() ==0) {
        WScript.Echo("No parameters found");
      }
      else {
        var objArgs = WScript.Arguments;
        var parms = ""
        for (i=0, n=objArgs.length; i<n; i++) {
          parms += '\nParam '+i+'='+objArgs(i);
        }
        WScript.Echo(parms);
      }


11.

How To Pass A Parameter to
a Perl script from the command line ActivePerl12 and Strawberry Perl13 both call MSVCRT.dll!__getmainargs which follows the Microsoft C/C++ Parameter Parsing Rules. Therefore the steps are:
  1. Start with the parameter you want
  2. Apply the Microsoft C/C++ parsing rules that ShowParams.exe will apply when parsing the command line it retrieves  [sec. 5]
  3. Apply the command line parsing rules that cmd.exe will apply when you enter the command and parameters  [sec. 7]
See sec. 8: How To Pass A Parameter to: a C/C++ Program from the Command Line above.

Sample 'Show Parameters' Perl script:
  • ShowParams.pl
    • if ( $#ARGV < 0 ) {
        print "No parameters\n";
      }
      else {
        my $i = 0;
        my $Parms = "";
        while ( $i &lt;= $#ARGV ) {
          $Parms .= "Param $i = $ARGV[$i]\n";
          $i ++;
        }
        print $Parms;
      }


12.

How To Pass A Parameter to
a Python script from the command line
Python™14 is written in C and obtains its parameters via the argv[] vector.15 It thus uses the Microsoft C/C++ Parameter Parsing Rules. Therefore the steps are:
  1. Start with the parameter you want
  2. Apply the Microsoft C/C++ parsing rules that ShowParams.exe will apply when parsing the command line it retrieves  [sec. 5]
  3. Apply the command line parsing rules that cmd.exe will apply when you enter the command and parameters  [sec. 7]
See sec. 8: How To Pass A Parameter to: a C/C++ Program from the Command Line above.

Sample 'Show Parameters' Python script for Python version 2:
  • ShowParams2.py
    • #!/usr/bin/python
      # Filename: using_sys.py
      import sys
      print 'The command line arguments are:'
      for i in sys.argv:
              print i
      print '\n\nThe PYTHONPATH is', sys.path, '\n'
Sample 'Show Parameters' Python script for Python version 3:
  • ShowParams3.py
    • #!/usr/bin/python
      # Filename: using_sys.py
      import sys
      print( 'The command line arguments are:' )
      for i in sys.argv:
              print( i )
      print( '\n\nThe PYTHONPATH is', sys.path, '\n' )


13.

How To Pass A Parameter to
a REXX script from the command line
8/24/2009 Under Construction...

Sample 'Show Parameters' REXX script:
  • ShowParams.rex
    • NumParams = %0%
      Param1 = %1%
      Param2 = %2%
      Param3 = %3%
      MsgBox GetCommandLine=%string%`nNumParams = %0%`nParam1 = %1%`nParam2 = %2%`nParam3 = %3%
      ExitApp


13.

How To Pass A Parameter to
a RUBY script from the command line
Ruby attempts to mimic the Microsoft C/C++ parameter parsing rules, but the current versions, 1.8.6 & 1.9.1, have a bug in them so they don't work properly.

(file Win32.c routine rb_w32_cmdvector, the memcpy statement needs to copy one more character to include the trailing NULL.)

Sample 'Show Parameters' RUBY script:
  • ShowParams.rb
    • ARGV.each do|a|
        puts "Argument: #{a}"
      end


15.

How To Pass A Parameter to
an AutoHotkey script from the command line
AutoHotkey is written in C++ and obtains its parameters via the __argv[] vector. Thus it uses the Microsoft C/C++ Parameter Parsing Rules. Therefore the steps are:
  1. Start with the parameter you want
  2. Apply the Microsoft C/C++ parsing rules that ShowParams.exe will apply when parsing the command line it retrieves  [sec. 5]
  3. Apply the command line parsing rules that cmd.exe will apply when you enter the command and parameters  [sec. 7]
See sec. 8: How To Pass A Parameter to: a C/C++ Program from the Command Line above.

Sample 'Show Parameters' AutoHotkey script:
  • ShowParams.ahk

    • NumParams = %0%
      Param1 = %1%
      Param2 = %2%
      Param3 = %3%
      result := DllCall("kernel32\GetCommandLineW")
      pointer := result
      string := DllCall("MulDiv","int",pointer,"int",1,"int",1,"str")
      MsgBox GetCommandLine=%string%`nNumParams = %0%`nParam1 = %1%`nParam2 = %2%`nParam3 = %3%
      ExitApp






David Deley © 2009
http://daviddeley.com

Last update: January 1, 2016







via daviddeley.com http://daviddeley.com

June 6, 2019 at 04:39PM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant