windowsbatch-filecmdprocessscripting

Syntax error when trying to execute a batch script file (*.cmd) with an additional script parameter in CMD


Prompt

I'm encountering a syntax error when trying to execute a batch script file (*.cmd) with an additional script parameter, directly in a CMD instance.

Specifics

This command works without any issues:

cmd.exe /C " START /W /MIN "Title" "_Path with spaces.cmd" "

However, when I try to pass a parameter to the script file, like this:

cmd.exe /C " START /W /MIN "Title" "_Path with spaces.cmd" "file parameter one" "

I get the error:

"_Path" is not recognized as an internal or external command, operable program or batch file.

Clearly there is a syntax error related to the usage of double quotes (maybe I'm missing to add additional double quotes somewhere else and/or to escape them), since the error message stops printing the file name on the first white space found.

Question

How can I correct the syntax to successfully pass the parameter "file parameter one" to the script file "_Path with spaces.cmd"?

Notes


Solution

  • Brief task description

    There should be executed a Windows batch file from within a Command Prompt window or from within a PowerShell console window which runs another batch file of which output should not be seen in the current console window.

    Solution 1 using command CALL

    That can be achieved by adding to the existing batch file:

    setlocal EnableExtensions DisableDelayedExpansion
    call "_Path with spaces\Other Batch.cmd" "file parameter one" 1>NUL 2>NUL
    endlocal
    

    There should be used the following three command lines if the batch file with spaces in the fully qualified file name is for sure always in the same directory as the batch file calling it:

    setlocal EnableExtensions DisableDelayedExpansion
    call "%~dp0Other Batch.cmd" "file parameter one" 1>NUL 2>NUL
    endlocal
    

    That simple solution works as long as neither the fully qualified batch file name nor the file name passed to the batch file contains one or more literally to interpret % characters and the other batch file does not contain the command exit without option /B.

    Solution 2 using command START

    An alternative and always working solution is:

    @start "Title" /MIN /WAIT %ComSpec% /D /S /C ^""_Path with spaces\Other Batch.cmd" "file parameter one"^" 
    

    That solution using an additional cmd.exe for processing Other Batch.cmd with cmd.exe processing the main batch file halting the batch file processing until the additionally started cmd.exe closed itself works even with one or more literally to interpret percent characters in one of the two file names and even if the command exit without option /B is used in Other Batch.cmd.

    Full explanation of both solutions

    A Command Prompt window is a running instance of the Windows Command Processor %SystemRoot%\System32\cmd.exe which is usually started from the Windows shell (explorer.exe) by calling the Windows kernel library function CreateProcess with a filled out STARTUPINFO structure.

    The parameters for the function call and the values for the structure are loaded in this case from the properties of the shortcut file %APPDATA%\Microsoft\Windows\Start Menu\Programs\System Tools\Command Prompt.lnk. The shortcut property Target is the string passed with function parameter lpCommandLine (long pointer to command line string) to CreateProcess. The shortcut property Start in is the string passed with function parameter lpCurrentDirectory (long pointer to current working directory string) to CreateProcess. It is quite easy to see which shortcut property on which tab is for which function parameter or structure element after reading the documentation for the function CreateProcess and the structure STARTUPINFO. If cmd.exe is executed from Windows shell without using the shortcut file, the console properties are loaded from Windows registry key HKEY_CURRENT_USER\Console.

    The start of Windows PowerShell %SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe is usually done by the Windows shell by calling CreateProcess with the parameters defined by the shortcut file %APPDATA%\Microsoft\Windows\Start Menu\Programs\Windows PowerShell\Windows PowerShell.lnk.

    The Microsoft developer blog Understanding Windows Console Host Settings offers more information about the settings used on starting a console application.

    The Windows kernel function CreateProcess is also called by cmd.exe and powershell.exe whenever an executable must be run as entered by a user directly in the console window on a command line, or read from the argument strings list on cmd.exe or powershell.exe being started with appropriate arguments, or read from a script file during processing of a Windows batch file or PowerShell script.

    The execution of cmd /? in a Command Prompt or PowerShell console window results in output of the usage help of the Windows Command Processor which explains how a command line consisting of one or more argument strings and perhaps even command and redirection operators after option /C (run command line and close) or option /K (run command line and keep running) is interpreted by cmd.exe. There are several criteria which must be considered on instructing cmd running a complex command line.

    In a Command Prompt window – not in a PowerShell console window – can be executed call /? for information about how a batch file can call another batch file by using the cmd internal command CALL and how arguments can be referenced from within a batch file. The execution of start /? in a Command Prompt window outputs the usage help for the cmd internal command START which can be used to run an executable by calling CreateProcess with different values for some function parameters and some structure elements.

    The command START modifies some parameters on calling the function CreateProcess for the execution of an executable depending on the used options of command START in comparison to running the executable during batch file processing without usage of command START.

    START without the options /B and /WAIT results in starting the executable as separate process with its own STDIN, STDOUT, STDERR handles which is executed parallel to cmd.exe as detached process and with its own console window on started executable is a console application. The Windows Command Processor immediately continues processing the batch file after the start of the executable in this case. It is possible that cmd.exe terminates itself while the started executable is still running and continues running.

    START with option /WAIT results also in starting the executable as separate process with its own STDIN, STDOUT, STDERR handles and creation of a new console window on being a console application. But cmd.exe does not continue processing the batch file. It waits for the self-termination of the started executable before it continues processing of the batch file.

    START with option /B results in starting the executable with STDIN, STDOUT, STDERR handles of cmd.exe without the creation of a new console window on being a console application. The process is executed parallel to cmd.exe which continues processing the batch file. But the started executable is not completely detached from the cmd process as it shares its handles with it. It becomes more or less a child process of cmd.exe which in general is terminated when the cmd process terminates which started the executable with START and option /B. Starting multiple console applications with START and option /B which all output to STDOUT and STDERR causes a chaotic list of output text in the console window.

    There should be read my answer on How to call a batch file that is one level up from the current batch file directory? It explains briefly the four methods which exist for running a batch file from within a batch file and what are the main differences.

    The called batch file could delete environment variables or redefine existing environment variables. It could also change the current working directory. If that is possible but not wanted for the main batch file execution, the commands SETLOCAL and ENDLOCAL should be used as shown above.

    The command SETLOCAL

    This is always done by the command SETLOCAL independent on being run with no, one or two parameters.

    The command ENDLOCAL

    This is done also by the Windows Command Processor cmd.exe implicitly for each SETLOCAL executed during processing of a batch file for which no ENDLOCAL execution was done explicitly before exiting the processing of a batch file. Many batch files using SETLOCAL somewhere at top do not contain ENDLOCAL somewhere at the end for that reason.

    The usage of SETLOCAL before calling the other batch file and ENDLOCAL after the batch file call makes sure that the execution environment (environment variables and current working directory) of the main batch file is not modified by the other batch file.

    There are just two commands in the called batch file which could affect the further execution of the main batch file other than pause.

    1. The command echo on is used in the called batch file to turn on the command echo mode. In this case it would be additionally necessary to insert @echo off after the call of the other batch file for turning off again the command echo mode.
    2. The command exit without option /B is used in the called batch file to instruct cmd.exe processing the batch file to immediately exit independent on calling hierarchy and if cmd.exe was started with option /C or option /K or none of these two options. In this case the main batch file is not further processed too. That is the reason nearly no batch file should contain ever the command exit without option /B as it makes the usage of this batch file from within another batch file more complicated than necessary. The exception is a batch file of which processing is started by an installer executable which deletes itself with the last command line in the batch file to prevent an error message output by cmd.exe on batch file suddenly not existing anymore.

    The first solution for the second point is modifying the called batch file and removing exit on being completely unnecessary and counterproductive on last line of the batch file or inserting the option /B after exit on being used in other lines of the called batch file for an immediate exit of the processing of this batch file without exiting the command process.

    The second solution is the one asked for. The command START is used to process the batch file by one more cmd.exe as separate command process and wait for its self-termination which can be also caused by the command exit without option /B in the other batch file.

    start "Title" /MIN /WAIT %ComSpec% /D /S /C ^""_Path with spaces\Other Batch.cmd" "file parameter one"^"
    

    There is started one more command process by referencing the system environment variable ComSpec which is defined by Windows default with %SystemRoot%\system32\cmd.exe by cmd.exe processing the main batch file with calling the Windows kernel function CreateProcess. There is created a new console window for this additional command process which is opened minimized. The cmd.exe instance processing the main batch file waits for the self-termination of the additionally started cmd.exe before it continues processing of the main batch file.

    There are passed to this additional command process the options /D for ignoring the AutoRun registry value and the option /S to interpret everything after option /C as one command line with removal of the first and the last " from this command line string.

    The two double quotes surrounding the entire command line with the fully qualified batch file name enclosed in " and the file name without or with path passed to the batch file also enclosed in " as required for file names with one or more spaces or one of these characters &()[]{}^=;!'+,`~ are escaped in the batch file with caret character ^ to be interpreted as literal characters by cmd.exe processing the main batch file. That is necessary if one of the two file names contains an ampersand. cmd.exe processing the batch file should parse "_Path with spaces\Other Batch.cmd" as one double quoted argument string and "file parameter one" as another double quoted argument string to interpret spaces and these characters &()[]{}^=;!'+,`~ inside the two file name strings also literally on processing the batch file like the later executed additional cmd.exe during the main batch file processing.

    The reason for using ^ left to first and last " of the command line parsed to the additional cmd.exe on execution of the main batch file is easier to understand with an example.

    There is the directory C:\Temp which contains:

    1. A batch file with name Main.cmd with the single command line:

      @start "Other Batch" /MIN /WAIT %ComSpec% /D /S /C ""%~dp0Development & Test 100%% (!)\Other.cmd" "file parameter one""
    2. A batch file with name Test.cmd with the single command line:

      @echo Arguments: %*& pause
    3. A subdirectory with name Development & Test 100% (!) with a batch file Other.cmd with the command line:

      @echo First argument is: %1& pause

    Open a Command Prompt window, run cd /D C:\Temp and execute now Main.cmd.

    What can be seen?

    1. There is for a truly short time opened minimized another console window with Other Batch as window title. It is impossible to see what is output in this console window as it closes immediately.

    2. There is displayed in the Command Prompt window the text:

      Arguments: 100% (!)\Other.cmd" "file parameter one""
      

    Press any key to continue . . .

    What happens here is that cmd.exe processing C:\Temp\Main.cmd interprets the command line as a line with two commands because of & outside a double quoted argument string as:

    @start "Title" /MIN /WAIT C:\WINDOWS\system32\cmd.exe /D /S /C ""C:\Temp\Development
    Test 100%% (!)\Other.cmd" "file parameter one""
    

    The command START is executed first by cmd.exe processing the batch file C:\Temp\Main.cmd which results in starting one more cmd.exe which fails to find an executable or script with name Development in the directory C:\Temp, outputs an error message in minimized console window and closes itself.

    Next is executed by cmd.exe processing the batch file C:\Temp\Main.cmd the second command Test by searching first in the current directory C:\Temp for such a file. It finds by chance Test.cmd and continues the batch file processing with processing of C:\Temp\Test.cmd. This batch file outputs on processing what it gets passed as arguments and then pauses the batch file processing until a key is pressed by the user.

    Now let us fix this unwanted behavior by modifying the line in C:\Temp\Main.cmd to:

    @start "Other Batch" /MIN /WAIT %ComSpec% /D /S /C ^""%~dp0Development & Test 100%% (!)\Other.cmd" "file parameter one"^"
    

    There are just ^ added left to first and last " of the command line to execute by the additional started cmd.exe.

    The execution of Main.cmd in C:\Temp in the Command Prompt windows results now in:

    1. One more console window with Other Batch as window title is opened and displayed minimized.
    2. The Command Prompt window shows nothing, but it is not possible to enter a command as cmd.exe is waiting for self-termination of the started second cmd.exe.

    Open the console window with window title Other Batch and there can be seen:

    First argument is: "file parameter one"
    Press any key to continue . . .
    

    Press any key in this console window and the window closes. The initial Command Prompt window shows now the prompt and there can be entered the next command.

    This small example demonstrates how difficult it is to write a batch file with a command line which should always work for any file/folder name which is interpreted during the batch file execution by more than one cmd.exe. The syntax rules of all cmd.exe must be considered on writing the command line in the main batch file.

    It is in most use cases better to use in C:\Temp\Main.cmd:

    @echo off
    setlocal EnableExtensions DisableDelayedExpansion
    call "%~dp0Development & Test 100%%%% (!)\Other.cmd" "file parameter one"  0<NUL 1>NUL 2>NUL
    @echo off
    endlocal
    

    The single % in the awful subdirectory name must be escaped with one more % in the batch file to be processed literally by cmd.exe processing C:\Temp\Main.cmd. But the usage of the command CALL results in parsing the command line a second time by cmd.exe which would remove the single % and the batch file Other.cmd would not be found anymore in the directory C:\Temp\Development & Test 100 (!) because of this directory with no % does not exist at all. The solution is %%%% which becomes %% on first parsing of the command line and finally just % on second parsing because of CALL.

    0<NUL results in no STDIN available on processing C:\Temp\Development & Test 100% (!)\Other.cmd and therefore pause does not result in halting the batch file execution until a key is pressed by the user.

    The execution of Other.cmd from C:\Temp with the command line "Development & Test 100% (!)\Main.cmd would also fail if C:\Temp\Main.cmd with the above command lines would be moved into the directory C:\Temp\Development & Test 100% (!) and the third command line would be modified to:

    call "%~dp0Other.cmd" "file parameter one" 0<NUL 1>NUL 2>NUL
    

    The moved batch file is executed with "Development & Test 100% (!)\Main.cmd" from the directory C:\Temp.

    The Windows Command Processor first parses this command line and interprets it as:

    call "F:\Temp\Development & Test 100% (!)\Other.cmd" "file parameter one"
    

    That looks right. But the command CALL results in parsing this command line a second time and it becomes now:

    call "F:\Temp\Development & Test 100 (!)\Other.cmd" "file parameter one"
    

    The character % in directory name is missing now after second command line parsing before the execution of the command CALL.

    In such uses cases with one of the file names could contain by chance a percent sign as part of the file name and not as part of an environment variable, loop variable or argument string reference, it is better to use the solution with command START instead of a solution with command CALL.