Search code examples
windowsbatch-filecommentspipeline

Why does a rem impact a code block that has its output piped?


I love using blocks of commands enclosed in parentheses, as it allows me to pipe the output of all commands in the block to more (or any other command) at once.

Usually, I can add remarks (rem) inside these blocks, too.

However, I cannot seem to combine the two features: a block with remarks piped to more (or any other command) is not executed at all. Why is that? Can it be solved?

@echo off
rem This works (prints X1)
(
    echo X1
)

rem This works (prints X2)
(
    echo X2
) | more

rem This works (prints X3)
(
    rem
    echo X3
)

rem Why does this print nothing?
(
    rem
    echo X4
) | more

rem Or this?
(
    echo X4
    rem
) | more

rem This works (prints X5)
(
    :: Using colons instead of rem
    echo X5
) | more

Solution

  • It's because of the folding.
    Using pipes results in two separate child cmd.exe processes.
    But the code has to be folded before it's transferred to a cmd.exe, especially code blocks.

    The folding rule is simple, each new line will be translated into an ampersand.
    So a folded block looks like

    ( echo X1 & echo X2 )
    

    But with a REM you get

    ( REM & echo X4 ) 
    

    The REM remarks the rest of the line, the ampersand has no effect anymore and the closing parenthesis is also remarked.

    If the REM is after that ECHO, it's the same problem

    ( echo X4 & REM ) 
    

    The last part is remarked, so there is only ( echo X4 and the closing parenthesis is missing.

    But why :: comments work?
    It looks like :: should result in the same error like REM, but it works!
    It's because :: labels are completely stripped from a code block, so the resulting line for a block like

    ( 
      :: My comment
      echo X4
    )
    

    Results in simply:

    C:\Windows\system32\cmd.exe  /S /D /c" ( echo X4 )"
    

    How to make REM work in piped blocks?

    The simple way is to use REM., but it's no quite a real REM, it can't comment out ampersand or other special characters.

    ( 
      REM. This is a comment, special characters aren't allowed here
      echo This works
    ) | more
    

    Or use an extra line feed in the REM line

    (set \n=^
    %=THIS LINE IS REQUIRED=%
    )
    ( 
      REM This is a comment with & and | %%\n%% BREAK
      echo This works
    ) | more
    

    How to debug folded blocks?

    It's simple with the cmdcmdline pseudo variable

    (
      echo Debug %%cmdcmdline%%%
      echo one
      echo two
    ) | findstr /n "^"
    

    Result:

    1:Debug C:\Windows\system32\cmd.exe /S /D /c" ( echo Debug %cmdcmdline% & echo one & echo two )"
    2:one
    3:two

    See also Why does delayed expansion fail when inside a piped block of code?