简体   繁体   中英

Why doesn't @ECHO ON/OFF work within a batch file IF block?

@echo on and @echo off don't seem to have any affect when executed within a bracketed if block in a batch file. Here's a simple demo:

@echo off
echo Test #1
if 1 == 1 (
  @echo on
  echo Test #2
  @echo off
  echo Test #3
)
@echo on
echo Test #4

The output from running the above on the command line is:

Test #1
Test #2
Test #3
C:\mybatchfilelocation>echo Test #4
Test #4

Could anyone explain this and/or suggest a workaround? (Expect it could probably be fixed by copious use of goto and labels but would prefer to continue using bracketed if blocks if possible...)

As you have discovered, the changed ECHO state is not recognized by the parser until it reaches a statement that is after the code block that contains the ECHO ON/OFF.

But there is one exception - the commands after FOR... DO do take on the state change within the same block:-)

Note that you only need @ to suppress command output when ECHO is currently ON. If it is OFF, then there is no need for @ECHO ON . And if you turn it ON and OFF within the same code block, then you don't need it there either.

Here is a demo that echos the even test lines:

@echo off
echo Test #1
(
  echo on
  for %%. in (.) do echo Test #2
  echo off
  echo Test #3
  echo on
  for %%. in (.) do echo Test #4
  echo off
  echo Test #5
)
echo on
echo Test #6
@echo off
echo Test #7

-- OUTPUT --

Test #1

C:\test>echo Test #2
Test #2
Test #3

C:\test>echo Test #4
Test #4
Test #5

C:\test>echo Test #6
Test #6
Test #7

You might find it convenient to declare a simple echo_on "macro". The following produces the exact same output:

@echo off
setlocal

set "echo_on=echo on&for %%. in (.) do"

echo Test #1
(
  %echo_on% echo Test #2
  echo off
  echo Test #3
  %echo_on% echo Test #4
  echo off
  echo Test #5
)
echo on
echo Test #6
@echo off
echo Test #7

Have just found out what is causing this by turning the echo on before an if block.

@echo on
if 1 == 1 (
  echo Test #1
  echo Test #2
)

This outputs:

C:\mybatchfilelocation>if 1 == 1 (
echo Test #1
echo Test #2
)
Test #1
Test #2

So the statement that is echoed is the entire if block rather than each statement within it. This answer explains this further and this answer gives a workaround - unfortunately not what I wanted to hear but it looks like lots of goto s and labels may be the only workable solution.

You could do something like this to achieve the result you want:

@echo off
set "ExecuteCmd=echo Test #2"
echo Test #1
if 1 == 1 (
  echo %ExecuteCmd%
  %ExecuteCmd%
  echo Test #3
)
@echo on
echo Test #4

How about putting the echo control commands inside a subroutine?

@echo off
echo Test #1
if 1 == 1 (
  CALL :DO_ECHO_ON
  echo Test #3
)
@echo on
echo Test #4

@EXIT /B

:DO_ECHO_ON
@ECHO ON
echo Test #2
@ECHO OFF
@EXIT /B

This produces the output I'd expect from your description.

Test #1

C:\secret>echo Test #2 
Test #2
Test #3

C:\secret>echo Test #4 
Test #4

Based on one of your comments, I suppose you want to be able to turn echo on for arbitrary commands. This modified version does that.

@echo off
echo Test #1
if 1 == 1 (
  CALL :DO_ECHO_ON_CMD echo Test #2
  echo Test #3
  CALL :DO_ECHO_ON_CMD dir /b "C:\Program Files"
  echo Test #3.1
  CALL :DO_ECHO_ON_CMD attrib c:\Windows
)
@echo on
echo Test #4

EXIT /B

:DO_ECHO_ON_CMD
@ECHO ON
%*
@ECHO OFF
@EXIT /B 0

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