Search code examples
windowsbatch-filecmd

Spaces in batch script without breaking the syntax


Is there a way in a batch script to add multiple spaces at the beginning of each line without breaking the syntax and keeping the spaces on each line as they are as per example below?

   gacutil /u ^
   Microsoft.IdentityModel.Clients.ActiveDirectory,^
   Version=2.28.0.725,^
   Culture=neutral,^
   PublicKeyToken=31bf3856ad364e35

The goal is to allow better readability not only when utilizing gacutil.exe but whenever writing a batch command


Solution

  • Every executable has its own rule set for splitting up the string after the executable file name into argument values being processed further by the executable.

    The windows command processor cmd.exe interprets a horizontal tab, a normal space, a comma, a semicolon, an equal sign and an OEM encoded no-break space (byte with hexadecimal value FF) outside a double quoted string as argument separators as it can be seen on running a batch file with name test.cmd with just the command line @echo %0 $1 $2 $3 $4 from within a command prompt window with

    test argument1,,,"argument 2";;;"argument 3 contains a comma ',' a semicolon ';' an equal sign '=' and multiple spaces"===argument4
    

    resulting in the output

    test argument1 "argument 2" "argument 3 contains a comma ',' a semicolon ';' an equal sign '=' and multiple spaces" argument4
    

    ,,, and ;;; and === are interpreted as argument string separators.

    The global assembly cache tool gacutil.exe splits up the string passed to it to argument strings different in comparison to cmd.exe. Commas and equal signs are not interpreted as argument separators. Only the normal space character is interpreted as argument separator, except a normal space is inside a double quoted argument string.

    It is common for Windows executables that the number of argument separators between argument strings does not matter. So if there is used just one space or multiple spaces between two argument strings does not matter.

    What does happen on using in a batch file following lines?

       gacutil /u ^
       Microsoft.IdentityModel.Clients.ActiveDirectory,^
       Version=2.28.0.725,^
       Culture=neutral,^
       PublicKeyToken=31bf3856ad364e35
    

    Please read first: How does the Windows Command Interpreter (CMD.EXE) parse scripts?

    The Windows command processor cmd.exe reads a batch file line by line with replacing carriage return + line-feed by just a line-feed on reading a line.

    The caret character ^ at end of a line is interpreted by cmd.exe as an escape character for the newline character line-feed resulting in concatenating the next line with the current line which is repeated until either reaching end of the batch file or finding a line-feed not escaped with ^. There are no characters removed during the concatenation of lines in a batch file.

    The result for the lines above in the batch file with three leading normal spaces on each line is:

    gacutil /u    Microsoft.IdentityModel.Clients.ActiveDirectory,   Version=2.28.0.725,   Culture=neutral,   PublicKeyToken=31bf3856ad364e35
    

    The three leading spaces left to gacutil at beginning of the command line are removed by cmd.exe, but all other spaces are kept because of cmd.exe cannot know if they are important for the program/script to run next or not. So there are four spaces after /u and three spaces between the other strings specified on separate lines in the batch file.

    gacutil.exe interprets the command line now as list of following arguments:

    1. /u
    2. Microsoft.IdentityModel.Clients.ActiveDirectory,
    3. Version=2.28.0.725,
    4. Culture=neutral,
    5. PublicKeyToken=31bf3856ad364e35

    The result is an error message because of gacutil.exe must be run in this case with just two arguments:

    1. The option /u to instruct the global assembly cache tool to uninstall an assembly from the global assembly cache
    2. and the assembly name Microsoft.IdentityModel.Clients.ActiveDirectory,Version=2.28.0.725,Culture=neutral,PublicKeyToken=31bf3856ad364e35.

    So the indenting spaces used in the batch file between the individual parts of the assembly name argument string not removed by cmd.exe makes the command line invalid for gacutil.exe.

    For that reason it is not possible in this case to define the assembly name argument string on multiple lines with indenting spaces because of how cmd.exe concatenates the lines with keeping the spaces and how gacutil.exe splits up the string passed to it from cmd.exe into arguments.

    A different executable could do the string splitting to a list of argument values different and for that reason writing the arguments of the executable on multiple lines in a batch file with indent spaces could work for this executable.

    But the following syntax can be used in a batch file for this use case:

       gacutil /u ^
       ^"Microsoft.IdentityModel.Clients.ActiveDirectory,^
       Version=2.28.0.725,^
       Culture=neutral,^
       PublicKeyToken=31bf3856ad364e35^"
    

    The global assembly cache tool has no problems with one or more spaces after a comma in assembly name as long as the entire assembly name argument string is enclosed in double quotes. For that reason there is a " at beginning and one more " at the end of the assembly name string. But the two double quotes must be escaped with ^ for the Windows command processor as otherwise cmd.exe would interpret the caret character ^ at end of each line as literal character and not as an escape character.

    The command line spread over multiple lines must be interpreted by cmd.exe as a line with no argument string in double quotes to get each ^ interpreted as escape character while the assembly name is in real enclosed in double quotes on running finally gacutil.exe with the command line:

    gacutil /u    "Microsoft.IdentityModel.Clients.ActiveDirectory,   Version=2.28.0.725,   Culture=neutral,   PublicKeyToken=31bf3856ad364e35"
    

    Yes, that is strange.