echo is adding space when used with a pipe

前端 未结 2 1750
栀梦
栀梦 2020-12-20 15:06

While answering this question I found some strange behavior for which I have no explanation

for /f \"delims=\" %a in (\'(for /l %z in (1,1,10^) do @echo %z^)         


        
相关标签:
2条回答
  • 2020-12-20 15:15

    Excellent and interesting question (+1)

    The space is introduced by the pipe mechanism of the CMD parser, not by SORT.

    When you execute a command using FOR /F, the command is executed in its own CMD shell. Also, each side of a pipe is executed in its own CMD shell. See Why does delayed expansion fail when inside a piped block of code? for more info.

    So your command actually instantiates 3 CMD shells, one for the FOR /F command, which in turn creates 2 for each side of the pipe.

    You can see how the commands get parsed and fed into the CMD shell using the %CMDCMDLINE% dynamic variable. Because we are executing the command from the command line, we need to escape at least one character in the variable name twice so that it doesn't get expanded until it reaches the inner most CMD shell.

    Here is the command with the results (the leading > is my command prompt):

    >for /f "delims=" %a in ('(echo %^^^cmdcmdline%^&for /l %z in (1,1,10^) do @echo %z^)^|sort') do @echo %a0
    1 0
    10 0
    2 0
    3 0
    4 0
    5 0
    6 0
    7 0
    8 0
    9 0
    C:\Windows\system32\cmd.exe  /S /D /c" ( echo %cmdcmdline% & FOR /L %z in (1 1 10) do @ echo %z )" 0
    

    The last line of output is the command line used for the left side of the pipe. You can see how the parser added spaces in a number of places.

    You can circumvent the problem by using a simple batch script to echo the value instead of the ECHO command.

    echoArgs.bat

    @echo(%*
    

    Now when you run this command you get the desired result

    >for /f "delims=" %a in ('(for /l %z in (1,1,10^) do @echoArgs %z^)^|sort') do @echo %a0
    10
    100
    20
    30
    40
    50
    60
    70
    80
    90
    

    Another method to circumvent the problem is to create a variable with your ECHO command and escape the expansion of the variable appropriately.

    >set cmd=@(echo %z)
    
    >for /f "delims=" %a in ('(for /l %z in (1,1,10^) do %^^^cmd%^)^|sort') do @echo %a0
    10
    100
    20
    30
    40
    50
    60
    70
    80
    90
    

    EDIT

    The >_tempfile echo and here's a space|more example is also interesting. There is an extra space at the end of the output file. But Chad Nouis is correct that nothing is being sorted because of the redirection of the left side. Any command could be used on the right side and the result would be the same.

    The source of the problem is still the parser, but the way the parser restructures the commmand is interesting.

    >>_tempfile echo %^cmdcmdline%|rem
    
    >type _tempfile
    C:\Windows\system32\cmd.exe  /S /D /c" echo %cmdcmdline% 1>_tempfile"
    

    Notice how the redirection is moved from the beginning to the end of the command, and the file handle of 1 is explicitly added. You can certainly see where the extra space comes from.

    0 讨论(0)
  • 2020-12-20 15:27

    Redirection operators apply to the command they are closest to--not the entire line.

    In your three test cases, you're redirecting the stdout of the echo and set commands to a file. By redirecting stdout, there is nothing left to pipe to more or sort.

    In your nested for loop example, I think there are some unneeded parentheses causing the headaches. Try this instead:

    for /f "delims=" %a in ('for /l %z in (1,1,10^) do @echo %z^|sort') do @echo %a0
    
    0 讨论(0)
提交回复
热议问题