Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are spaces being inserted into this batch file at runtime?

Tags:

batch-file

cmd

I've got this simple test script:

echo start
falsey ^
&& echo nope
echo end

falsey is a tiny C program I made to help test, it simply returns 1:

int main(int argc, const char** argv) {
        return 1;
}

The problem is, when I open up cmd and run this batch script, it inserts a couple spaces between the two &s, which naturally causes it to incorrectly echo nope:

C:\jenkins-foo-test>foo.bat
C:\jenkins-foo-test>echo start
start
C:\jenkins-foo-test>falsey &  & echo nope
nope
C:\jenkins-foo-test>echo end
end

I have figured out a few workarounds for this, such as putting quotes around the &&:

falsey ^
"&&" echo nope

Quotes don't actually work in this case, see @dbenham's answer for why.

dropping the ^ and merging the two lines:

falsey && echo nope

or adding a space in front of the &&:

falsey ^
 && echo nope

Each of those three options correctly do not print nope. But they do all still have extra spaces when cmd.exe prints the commands it's running.

So why are these extra spaces being inserted and how can I stop cmd.exe from breaking things without having to contort my code into mildly unnatural shapes all the time?

like image 200
8bittree Avatar asked Jun 16 '16 20:06

8bittree


1 Answers

The line continuation ^ escapes the first character on the next line, so the first & is treated as a literal and is passed as an argument to falsey. The second & is treated as simple command concatenation.

One simple solution to get && to be treated as conditional command concatenation is to put the && on the line before:

falsey &&^
echo nope

or you could put a space before the &&

falsey ^
 && echo nope

The other option is to use the redirection trick that jeb points out in his linked answer, but I never use that trick.


Regarding your "solution" with quoted "&&" - you are fooling yourself. It appears to work when falsey fails. But if your command succeeds, then you have trouble:

echo ONE ^
"&&" echo TWO

-- OUTPUT --

C:\test>echo ONE "  && " echo TWO
ONE "
'" echo TWO' is not recognized as an internal or external command,
operable program or batch file.

Note that the && works because the first " is escaped, so the && is not quoted. If you add a space before the "&&"

echo ONE^
 "&&" echo TWO

then you get the following

C:\test>echo ONE "&&" echo TWO
ONE "&&" echo TWO

because now the space is escaped and the "&&" is quoted.


Regarding your comment, you can ignore those extra spaces - they are an artifact of how the parser displays the line when ECHO is ON. The parser often rearranges the line significantly, but normally it does not affect the outcome.

For example

   <    nul  set test=OK
echo [%test%]

--OUTPUT--

C:\test>set test=OK 0<nul

C:\test>echo [OK]
[OK]

Note how the echoed SET line is completely rearranged. The redirection is compressed and moved to the end of the statement, with a space before the redirection. You might think the space would be included in the assignment, but you can see that it is not :-)

The only time you have might have to worry about the rearrangement is if the command is used in a pipe.

For example:

(set test=OK&call echo [%%^^test%%])|findstr "^"

--OUTPUT--

C:\test>(set test=OK  & call echo [%^test%] )  | findstr "^"
[OK ]

You can see that there is a single unwanted extra space that is included in the SET value. This is an artifact of how pipes are implemented - each side is executed in a new CMD.EXE process, and the line gets parsed multiple times. You can see where the space comes from by using %CMDCMDLINE% to display the command line passed to the left side's cmd.exe.

(set test=OK&call echo [%%^^test%%] %%^^cmdcmdline%%)|findstr "^"

--OUTPUT--

C:\test>(set test=OK  & call echo [%^test%] %^cmdcmdline% )  | findstr "^"
[OK ] C:\WINDOWS\system32\cmd.exe  /S /D /c" ( set test=OK & call echo [%^test%] %^cmdcmdline% )"

See Why does delayed expansion fail when inside a piped block of code? for more information about many quirks with pipes. In particular, pay attention to the selected answer for a good explanation of what is going on.

like image 122
dbenham Avatar answered Feb 23 '23 01:02

dbenham