9

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?

Community
  • 1
  • 1
8bittree
  • 1,769
  • 2
  • 18
  • 25
  • 2
    What's the advantage of escaping a line break and putting `&&` on the next line, anyway? `falsey && echo nope` or `falsey && (\n echo nope\n)` (where `\n` are new lines) is standard convention. What you're doing feels like starting a sentence with a conjunction. And it's awkward. – rojo Jun 16 '16 at 20:18
  • What text editor are you using to edit your batch file? – Mr. DROP TABLE Jun 16 '16 at 20:22
  • @rojo Because the lines I actually want to use this for are quite long. Putting the `&&` at the beginning of the next line makes it clear its execution depends on that of the previous line. – 8bittree Jun 16 '16 at 20:22
  • @Mr.DROPTABLE Notepad++, with Windows line endings. Also the web interface in Jenkins for a separate instance. – 8bittree Jun 16 '16 at 20:24
  • @8bittree Just do `falsey && (` then on the next line `echo no` indented, then close the parenthetical code block on the line after that. If you're one of those Java programmers who opens and closes braces at the same number of tabs (putting the opening brace below the `if` statement for example), that doesn't really work with batch. The code block opener ought to be on the same line as its preceding... whatever. [See the description on this page](http://www.dostips.com/?t=Snippets.ConditionalExecution), the bit after "or:" just above the green script example. – rojo Jun 16 '16 at 20:27
  • @8bittree Alternatively, you could use `if errorlevel 1` to match an exit code greater than 0, or `if not %errorlevel% equ 0` to match any non-zero exit code (negative or positive). – rojo Jun 16 '16 at 20:31
  • Did you by anychance download this text editor from filezilla? – Mr. DROP TABLE Jun 16 '16 at 20:36
  • @Mr.DROPTABLE Pretty sure I got it from https://notepad-plus-plus.org . It has admittedly been a few years since I got the installer, but I prefer to get my tools from the official site or repository, rather than random file sharing sites. As for Jenkins, that I definitely got from http://jenkins-ci.org/ – 8bittree Jun 16 '16 at 20:43
  • I would suggest just taking a look at your default settings, making sure your Encoding is in ANSI (or whatever you want). I just asked because sometimes the default settings from Filezilla (or other download sites) give weird errors like that. Give it a try! – Mr. DROP TABLE Jun 16 '16 at 20:45
  • @Mr.DROPTABLE UTF-8 without byte order mark. – 8bittree Jun 16 '16 at 20:46
  • 3
    Batch doesn't play nice with UTF-8. Try saving it as ANSI. – SomethingDark Jun 16 '16 at 20:51
  • What @SomethingDark said! That's what I was looking for – Mr. DROP TABLE Jun 16 '16 at 20:53
  • @SomethingDark The characters I'm using are identical in both ANSI and UTF-8. Switching to ANSI makes no difference here. – 8bittree Jun 16 '16 at 21:01
  • @rojo Handy to know, though I already had a few workarounds. And I'm still leaves me wondering *why* this is happening. – 8bittree Jun 16 '16 at 21:05
  • I can duplicate this in pure batch using ANSI encoding. It's probably related to how batch does line continuation, if I had to guess. – SomethingDark Jun 16 '16 at 21:11
  • @SomethingDark Seems more related to the `&&` (and maybe some other characters) than the line continuation. The one liner `falsey && echo nope` still has some extra spaces inserted between `falsey` and `&&`. – 8bittree Jun 16 '16 at 21:26

1 Answers1

9

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.

Community
  • 1
  • 1
dbenham
  • 127,446
  • 28
  • 251
  • 390
  • What about the extra spaces when there is no `^` and everything is one one line? – 8bittree Jun 17 '16 at 00:28
  • @8bittree - Those extra spaces only appear in the output when the command is echoed (ECHO is ON). They are an artifact of the parser. The spaces are not actually passed to the command, so they have no impact. The only time they have an impact is when the command is parsed multiple times and executed in a new cmd.exe process, such as when used in a pipe. – dbenham Jun 17 '16 at 00:42
  • @8bittree - Look at my last edit for a better explanation of the "extra spaces", as well as why you can normally ignore them. – dbenham Jun 17 '16 at 02:25