简体   繁体   中英

Batch file multiline command with double quotes

When using the ^ symbol to enter a multiline command with arguments when using double quotes to use strings with spaces the ^ symbol is also passed on, can anyone explain way this is?

working.cmd

@echo off 
call openfiles.cmd ^
C:\dir\filename.txt ^
C:\another_dir\another_file.txt

notworking.cmd

@echo off 
call openfiles.cmd ^
"C:\dir with spaces\file with spaces.txt" ^
"C:\another dir with spaces\another file with spaces.txt"

openfiles.cmd looks like

@echo off
for %%x in (%*) do (

    IF EXIST %%x (
        call "c:\Program Files\Notepad++\notepad++.exe" %%x
    ) ELSE (
        call echo Not found %%x
    )

)

pause

the error I get look like

C:\>call openfiles.cmd "C:\dir with spaces\file with spaces.txt" ^
ELSE was unexpected at this time.

The problem is, that the quote at the beginning of the second line will be escaped by the multi line caret.
Therefore the last quote in the line starts the quoting instead of stopping it and so the caret in the second line is handled as normal character.

call openfiles.cmd ^"C:\dir with spaces\file with spaces.txt" ^
**This is a seperate line** "C:\another dir with spaces\another file with spaces.txt"

The caret rule:

A caret escapes the next character, so that the character loses all special effects.
If the next character is a linefeed drop this and take the next character (even when this is also a linefeed).

With this simple rule you can explain things like

echo #1 Cat^&Dog
echo #2 Cat^
&Dog
echo #3 Redirect to > Cat^
 Dog

setlocal EnableDelayedExpansion
set linefeed=^


echo #4 line1!linefeed!line2

#3 creates a file named "Cat Dog" as the space was escaped and doesn't work as delimiter anymore.

But it's still possible to break this rule!
You only need to put any redirection just in front of the caret, it still drops the linefeed (multiline still works), but the next character isn't escaped anymore.

echo #5 Line1< nul ^
& echo Line2

So you could also use this to build your multiline command

call openfiles.cmd < nul ^
"C:\dir with spaces\file with spaces.txt" < nul ^
"C:\another dir with spaces\another file with spaces.txt"

Or using a macro

set "\n=< nul ^"
call openfiles.cmd %\n%
"C:\dir with spaces\file with spaces.txt" %\n%
"C:\another dir with spaces\another file with spaces.txt"

After trying some diffrent things I managed to get it working with just a extra space infront of the double quotes. Changes notworking.cmd to the following worked

@echo off 
call openfiles.cmd ^
 "C:\dir with spaces\file with spaces.txt" ^
 "C:\another dir with spaces\another file with spaces.txt"

note the spaces in front of the double quotes

in short:

" characters need to be ^ -escaped , otherwise cmd literally copies ^ characters appearing between " pairs .

Warning : ^ copies the character following the caret literally making it lose any metacharacter functionality if it had any. In a multi-line scenario, not only the newline/linefeed are copied literally but also the first character in the new line is also escaped

explanation:

The ^ caret metacharacter

Following definition is copied from a sub-section of the article "Everyone quotes command line arguments the wrong way" , by Daniel Colascione on April 23, 2011

A better method of quoting

While the " metacharacter cannot fully protect metacharacters in our command lines against unintended shell interpretation, the ^ metacharacter can. When cmd transforms a command line and sees a ^ , it ignores the ^ character itself and copies the next character to the new command line literally, metacharacter or not. That's why ^ works as the line continuation character: it tells cmd to copy a subsequent newline as itself instead of regarding that newline as a command terminator. If we prefix with ^ every metacharacter in an argument string, cmd will transform that string into the one we mean to use.

and now for the important part relevant to the presented problem:

It's important to also ^ -escape " characters : otherwise, cmd would literally copy ^ characters appearing between " pairs, mangling the argument string in the process.

Note that when you ^" , the " loses its functionality as quoting so be careful must be taken.


Demo:

without ^ -escaping "

"c:\Program^
 Files\test.bat"

becomes

"c:\Program^
Files\test.bat"

with ^ -escaping "

^"c:\Program^
 Files\test.bat^"

becomes

"c:\Program Files\test.bat"

and executes the program

Note that ^ -escaping the quotes makes them lose their quoting functionality so this command could also be executed as: c:\\Program^ Files\\test.bat . The reason why we normally place it in quotes is to remove the delimiting function of the space metacharacter, but caret escaping the space character provides the same functionality.

with ^ -escaping " , but space moved to previous line

^"c:\Program ^
Files\test.bat^"

becomes

"c:\Program Files\test.bat"

but the command line reports 'c:\\Program' is not recognized...

This is because with the space character on the first line and the " character ^ -escaped, the <space> is interpreted as a delimiter and not as a character.

With the second example the <space> is the first character in the newline after a ^ and is thereby escaped and interpreted as the character and not a delimiter.


Sources: Microsoft Blog article

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM