Search code examples
batch-filecolorsansi-escapemsys

Cmd.exe Batch Script Color Output With FOR /F 'MSYS' Commands


I've started utilizing ANSI color codes in batch scripts for Windows 10. Today I discovered that suddenly colors stopped working, and I traced it to a for command with an embedded MSYS command. For example:

@echo off
echo ^[[92mThis works.^[[0m
for %%f in (x) do @echo ^[[93mThis also works.^[[0m
for /l %%f in (1,1,1) do @echo ^[[94mThis too works.^[[0m
for /f "tokens=*" %%f in ('date /t') do @echo ^[[95mThis one works.^[[0m
for /f "tokens=*" %%f in ('sha1sum.exe ^< NUL') do @echo ^[[91mThis one DOES NOT work.^[[0m
echo ^[[92mThis works.^[[0m

(Note: Where you see "^[" needs to be a single ESC character, ASCII 27, instead. E.g., type <C-V><ESC> in Vim's insert mode.)

In my case, sha1sum.exe refers to C:\Program Files\Git\usr\bin\sha1sum.exe from Git for Windows. We all know batch scripts/cmd.exe have their own wizardry about them, but I can't think of an explanation for this. Is it somehow toggling color interpretation off for the duration of the command line? And if so would there be a way to force it back on?

APPEND:

It's worth noting that it seems to affect the entire "command line", meaning if you have nested ifs and fors using parenthesis the entire bit wrapped in parenthesis appears to be affected.


Solution

  • This is just a confirmation and some workarounds

    Sample code

    @echo off
        setlocal enableextensions disabledelayedexpansion
    
        for /f %%a in ('
            forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo 0x1B"
        ') do set "ESC=%%a"
    
        echo %ESC%[91mRed
        echo %ESC%[92mGreen
        echo %ESC%[0mNormal
    
        echo A simple base test 
        for /f "tokens=1,2" %%a in ('
            echo 0000000000000000000000000000000000000000 %~f0
        ') do echo(.... %ESC%[92m%%a %ESC%[91m%%b %ESC%[0m
    
        echo Git sha1sum
        for /f "tokens=1,2" %%a in ('
            E:\portable\bin\git\64\usr\bin\sha1sum.exe "%~f0" 
        ') do echo(.... %ESC%[92m%%a %ESC%[91m%%b %ESC%[0m
    
        echo Git sha1sum DELAYED
        for /f "tokens=1,2" %%a in ('
            E:\portable\bin\git\64\usr\bin\sha1sum.exe "%~f0" 
        ') do set "output=%ESC%[92m%%a %ESC%[91m%%b %ESC%[0m"
        echo(.... %output%
    
        echo GNUPG sha1sum  
        rem     ftp://ftp.gnupg.org/gcrypt/binary/sha1sum.exe 
        rem     https://lists.gnupg.org/pipermail/gnupg-announce/2004q4/000184.html
        for /f "tokens=1,2" %%a in ('
            E:\portable\bin\sha1sum.exe "%~f0"
        ') do echo(.... %ESC%[92m%%a %ESC%[91m%%b %ESC%[0m
    
        echo %ESC%[90mDone%ESC%[0m
    

    Output sample code output

    As seen, you can deal with a simple case, but the git sha1sum executable interferes with the console.

    Some alternatives

    • The easiest one is to send the output to a file and then use the for /f to process the file

    • Here you have information on a way to deal with the problem (you will need to compile code)

    • Select a different tool for the work.