26

In batch, can you have different colors per line. For example if you had a batch file say "Hello" "How are you?"

Could you have "Hello" in blue and "How are you" in green?

(I know about the color command, and the way it colors both the background and the text)

Gareth Jones
  • 1,241
  • 4
  • 17
  • 38
  • 3
    Duplicate of this (poorly titled) question: [how can i make a multi-functional batch program?](http://stackoverflow.com/questions/7290434/how-can-i-make-a-multi-functional-batch-program/7302678#7302678) – Hand-E-Food Oct 28 '11 at 00:31
  • @Hand-E-Food Thanks. I see what you mean by poor title, However is there a way to do this without having to set it up? I ask beacuse im making a batch game were this would be a nice feature, and so i want to be able to give it to someone without having to change any computer settings – Gareth Jones Oct 28 '11 at 00:36
  • 1
    You don't need to change computer settings. Just include this batch file in the same folder as your game. The poster of the other question wanted this to be system wide, so I added that into the answer. All you need is the script titled **CEcho.bat** – Hand-E-Food Oct 28 '11 at 00:41
  • 1
    Possible duplicate of [How to have multiple colors in a Windows batch file?](http://stackoverflow.com/questions/4339649/how-to-have-multiple-colors-in-a-windows-batch-file) – Charlie Apr 18 '17 at 07:43
  • [How can I use command line arguments to control a batch program that prints multi-colour text?](https://stackoverflow.com/q/7290434/995714) – phuclv Oct 27 '19 at 01:34

11 Answers11

30

You can do it without any external program. Here is something that I came over a couple of days ago. Works very good.

@echo off
SETLOCAL EnableDelayedExpansion
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
  set "DEL=%%a"
)
echo say the name of the colors, don't read

call :ColorText 0a "blue"
call :ColorText 0C "green"
call :ColorText 0b "red"
echo(
call :ColorText 19 "yellow"
call :ColorText 2F "black"
call :ColorText 4e "white"

goto :eof

:ColorText
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1
goto :eof
Credits to jeb
His post can be found here
Community
  • 1
  • 1
5

The Batch file below create COLORMSG.COM file (rename it as you wish) that show a message with color this way: colormsg color "Message".

@echo off
(
echo e100
echo 0F B6 0E 80 00 E3 4F BF 81 00 B0 20 FC F3 AE 74
echo e110
echo 45 E3 43 8A 45 FF E8 43 00 80 3D 20 74 0E C0 E0
echo e120
echo 04 8A E0 8A 05 E8 34 00 0A C4 47 49 E3 28 32 E4
echo e130
echo 50 B0 22 F2 AE 75 1F E3 1D 8B F7 8B D1 F2 AE 75
echo e140
echo 01 41 2B D1 74 10 8B CA 5B B0 20 B4 09 CD 10 AC
echo e150
echo B4 0E CD 10 E2 F9 32 C0 B4 4C CD 21 3C 61 72 02
echo e160
echo 2C 20 3C 41 72 02 2C 07 2C 30 C3
echo rcx
echo 6b
echo w
echo q
) | debug colormsg.com > nul

After the message the cursor is not moved to new line, so several messages may be shown in the same line. To give colors in an easier way, you may use these definitions:

set Black=0&       set Gray=8
set Blue=1&        set LightBlue=9
set Green=2&       set LightGreen=A
set Aqua=3&        set LightAqua=B
set Red=4&         set LightRed=C
set Purple=5&      set LightPurple=D
set Yellow=6&      set LightYellow=E
set White=7&       set BrightWhite=F

For example:

colormsg %Yellow% "Message in yellow  "
colormsg %Blue%%BrightWhite% "Bright white text over blue background"

This is a larger example:

@echo off
setlocal EnableDelayedExpansion
set i=0
for %%c in (Blk Blu Grn Aqu Red Pur Yel Whi) do (
    set color[!i!]=%%c
    set /A i+=1
)
for /L %%j in (0,1,7) do (
    set color[!i!]=L!color[%%j]:~0,-1!
    set /A i+=1
)    
cls
echo/
echo           BACKGROUND / FOREGROUND COMBINATIONS OF AVAILABLE SCREEN COLORS
echo/
echo/
colormsg 7 "ForeGrnd:"
for /L %%i in (0,1,15) do colormsg 7 " !color[%%i]!"
echo/
echo BackGrnd
set i=0
for /L %%b in (0,1,9) do call :ShowLine %%b
for %%b in (A B C D E F) do call :ShowLine %%b
echo/
goto :eof

:ShowLine
colormsg 7 "   !color[%i%]!   "
set /A i+=1
for /L %%f in (0,1,9) do colormsg %1%%f " %1%%f "
for %%f in (A B C D E F) do colormsg %1%%f " %1%%f "
echo/

Hey Gareth, here is a bonus for you! The following Batch file create TEXTPOS.COM file (rename it as you wish) that move the cursor to any line and column this way: textpos line col:

@echo off
(
echo e100
echo 0F B6 0E 80 00 E3 2D BF 81 00 B0 20 FC F3 AE 74
echo e110
echo 23 E3 21 8D 75 FF E8 45 00 3C 20 75 3B 86 F2 E8
echo e120
echo 3C 00 86 F2 3C 20 74 04 3C 0D 75 2C 32 FF B4 02
echo e130
echo CD 10 EB 24 32 FF B4 03 CD 10 8A C6 8A CA E8 38
echo e140
echo 00 B2 20 B4 02 CD 21 8A C1 E8 2D 00 B2 0D B4 02
echo e150
echo CD 21 B2 0A B4 02 CD 21 32 C0 B4 4C CD 21 32 E4
echo e160
echo AC 3C 20 74 FB 3C 30 72 0D 3C 39 77 09 2C 30 D5
echo e170
echo 0A 8A E0 AC EB EF 8A F4 C3 D4 0A 05 30 30 8B D0
echo e180
echo 80 FC 30 74 08 86 D6 B4 02 CD 21 8A D6 B4 02 CD
echo e190
echo 21 C3
echo rcx
echo 92
echo w
echo q
) | debug textpos.com > nul

For example:

textpos 0 0
colormsg %Red% "Red message at top left corner of the screen"
textpos 10 40
colormsg %Blue%%LightYellow% "This message start in line 10 column 40"

EDIT

I slightly modified TEXTPOS program so it display the current cursor position if it is executed with no parameters; this feature allows to store the cursor position in a variable this way: textpos > pipe.txt & set /P pos=< pipe.txt (because textpos | set /P pos= have an error and does not work).

If a color message is shown with the same value for background and foreground colors, the text will not be visible in the screen; this feature allows to enter a password. For example, this is getpassword.bat file:

@echo off
set password=
for /L %%i in (1,1,%1) do set password=!password!x
colormsg 7 "Enter password: "
textpos > pipe.txt & set /P passpos=< pipe.txt
colormsg 77 "%password%"
textpos %passpos%
set password=
for /L %%i in (1,1,%1) do (
    getakey
    set password=!password!!errorlevel!
    colormsg 7F "*"
)

Previous Batch file allows to read a password with the number of characters given in the parameter, for example: call getpassword 8; entered characters are changed by its ASCII code, so the password have a rudimentary encryption. For example, to check if the entered password was "Pass":

call getpassword 4
if %password% == 8097115115 goto right_password

I provided the GETAKEY.COM program in a previous question, but here is it again:

@echo off
(
echo e100
echo B4 08 CD 21 B4 4C CD 21
echo rcx
echo 8
echo w
echo q
) | debug getakey.com > nul
Aacini
  • 65,180
  • 12
  • 72
  • 108
  • I''ll appreciate it if the down voter may explain the reasons for the down vote... **`:(`** Please, note that this solution was posted on October 2011, when there was _not_ a 64-bits version of Windows and this method correctly works in all computers! – Aacini Oct 05 '17 at 14:45
  • I suggest you to precisely states the fact that the debug command is only available with 32 Bits Windows version on your answer, i guess this will prevent anger reactions. Using debug is a good idea, however dynamic generation of binaries by shell script for serving the shell script should always be a last resort. You can also adapt your answer using power shell to generate NE / PE / PE32 / PE32+ executables. But going this way, it's better to [directly use power shell to do the job](https://stackoverflow.com/a/66604961/3641635). – Zilog80 Mar 16 '21 at 20:05
  • (Another point regarding 64 Bits Windows, the first 64 Bits Windows 2003 Server and Windows XP 64 Bits were release in April 2005. In 2011, many Windows techs were already 64 Bits CPU+OS, as i was. In fact a 64 Bits bi-classed LFS/Windowsian. Today a 64 Bits tri-classed Debianist/Androindian/Windowsian.) – Zilog80 Mar 16 '21 at 20:16
3
@echo off

REM You can do it easily by typing this at the bottom of your file.

:[any color]
powershell -Command Write-Host "%*" -foreground "[any color]" -background "[any color]"

:[any color]
powershell -Command Write-Host "%*" -foreground "[any color]" -background "[any color]"

REM for typing text in different colors you just need to write this

call :[any color] "[any message]"
call :[any color] "[any other message]"
pause

exit
drhagen
  • 8,331
  • 8
  • 53
  • 82
3

Save this code as .bat (it is self-compiled .net hybrid):

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

for /f "tokens=* delims=" %%v in ('dir /b /s /a:-d  /o:-n "%SystemRoot%\Microsoft.NET\Framework\*jsc.exe"') do (
   set "jsc=%%v"
)

if not exist "%~n0.exe" (
   "%jsc%" /nologo /out:"%~n0.exe" "%~dpsfnx0"
)

%~n0.exe %*

endlocal & exit /b %errorlevel%

*/

import System;

var arguments:String[] = Environment.GetCommandLineArgs();

var newLine = false;
var output = "";
var foregroundColor = Console.ForegroundColor;
var backgroundColor = Console.BackgroundColor;
var evaluate = false;
var currentBackground=Console.BackgroundColor;
var currentForeground=Console.ForegroundColor;


//http://stackoverflow.com/a/24294348/388389
var jsEscapes = {
  'n': '\n',
  'r': '\r',
  't': '\t',
  'f': '\f',
  'v': '\v',
  'b': '\b'
};

function decodeJsEscape(_, hex0, hex1, octal, other) {
  var hex = hex0 || hex1;
  if (hex) { return String.fromCharCode(parseInt(hex, 16)); }
  if (octal) { return String.fromCharCode(parseInt(octal, 8)); }
  return jsEscapes[other] || other;
}

function decodeJsString(s) {
  return s.replace(
      // Matches an escape sequence with UTF-16 in group 1, single byte hex in group 2,
      // octal in group 3, and arbitrary other single-character escapes in group 4.
      /\\(?:u([0-9A-Fa-f]{4})|x([0-9A-Fa-f]{2})|([0-3][0-7]{0,2}|[4-7][0-7]?)|(.))/g,
      decodeJsEscape);
}


function printHelp( ) {
   print( arguments[0] + "  -s string [-f foreground] [-b background] [-n] [-e]" );
   print( " " );
   print( "   string          String to be printed" );
   print( "   foreground       Foreground color - a " );
   print( "               number between 0 and 15." );
   print( "   background       Background color - a " );
   print( "               number between 0 and 15." );
   print( "   -n               Indicates if a new line should" );
   print( "               be written at the end of the ");
   print( "               string(by default - no)." );
   print( "   -e               Evaluates special character " );
   print( "               sequences like \\n\\b\\r and etc ");
   print( "" );
   print( "Colors :" );
   for ( var c = 0 ; c < 16 ; c++ ) {

      Console.BackgroundColor = c;
      Console.Write( " " );
      Console.BackgroundColor=currentBackground;
      Console.Write( "-"+c );
      Console.WriteLine( "" );
   }
   Console.BackgroundColor=currentBackground;



}

function errorChecker( e:Error ) {
      if ( e.message == "Input string was not in a correct format." ) {
         print( "the color parameters should be numbers between 0 and 15" );
         Environment.Exit( 1 );
      } else if (e.message == "Index was outside the bounds of the array.") {
         print( "invalid arguments" );
         Environment.Exit( 2 );
      } else {
         print ( "Error Message: " + e.message );
         print ( "Error Code: " + ( e.number & 0xFFFF ) );
         print ( "Error Name: " + e.name );
         Environment.Exit( 666 );
      }
}

function numberChecker( i:Int32 ){
   if( i > 15 || i < 0 ) {
      print("the color parameters should be numbers between 0 and 15");
      Environment.Exit(1);
   }
}


if ( arguments.length == 1 || arguments[1].toLowerCase() == "-help" || arguments[1].toLowerCase() == "-help"   ) {
   printHelp();
   Environment.Exit(0);
}

for (var arg = 1; arg <= arguments.length-1; arg++ ) {
   if ( arguments[arg].toLowerCase() == "-n" ) {
      newLine=true;
   }

   if ( arguments[arg].toLowerCase() == "-e" ) {
      evaluate=true;
   }

   if ( arguments[arg].toLowerCase() == "-s" ) {
      output=arguments[arg+1];
   }


   if ( arguments[arg].toLowerCase() == "-b" ) {

      try {
         backgroundColor=Int32.Parse( arguments[arg+1] );
      } catch(e) {
         errorChecker(e);
      }
   }

   if ( arguments[arg].toLowerCase() == "-f" ) {
      try {
         foregroundColor=Int32.Parse(arguments[arg+1]);
      } catch(e) {
         errorChecker(e);
      }
   }
}

Console.BackgroundColor = backgroundColor ;
Console.ForegroundColor = foregroundColor ;

if ( evaluate ) {
   output=decodeJsString(output);
}

if ( newLine ) {
   Console.WriteLine(output);   
} else {
   Console.Write(output);

}

Console.BackgroundColor = currentBackground;
Console.ForegroundColor = currentForeground;
Community
  • 1
  • 1
npocmaka
  • 55,367
  • 18
  • 148
  • 187
3

You can try this just with a little bit of command:

@echo off
chcp 437>nul&&graftabl 936>nul
ren %WinDir%\System32\config.nt config.nt.bak 2>nul
<"%~f0" more +6 >%WinDir%\System32\config.nt
command /cecho [1;31mHel[32mlo [33mHow[35mare[36myou[m
pause>nul&exit
DOSONLY
dos=high, umb
device=%SystemRoot%\system32\himem.sys
DEVICE==%SystemRoot%\System32\ANSI.SYS /x
files=40
GaryNg
  • 1,103
  • 9
  • 16
2

Save it as colormsg.bat

@echo off
if "%~3" == "" goto usage
powershell -command write-host -foreground "%~1" -background "%~2" -nonewline "%~3"
exit /b
:usage
echo.
echo Usage: call colormsg foreground background "message"
echo.
echo Examples:
echo call colormsg white blue "example1"
echo call colormsg 4 2 "example2"

Example:

@echo off
for /f %%a in ('"prompt $H & for %%a in (-) do rem"') do set "BS=%%a"
call colormsg blue 0 "Hello"
<nul set /p "=.%BS% "
call colormsg green 0 "How are you?"
echo.
pause
TNian145
  • 31
  • 1
  • 4
1

Yes, it is possible with cmdcolor:

echo \033[94mHello
echo \033[92mHow are you
Alec Mev
  • 4,663
  • 4
  • 30
  • 44
  • Windows 10's conhost supports [setting color with ANSI sequences](https://superuser.com/a/1050078/241386), no need to use any other 3rd party tools – phuclv Oct 27 '19 at 01:37
1

You can try this simple script. It does not use temporary files. Just be sure you have the "debug.exe" executable in any folder listed in "%path%" environment variable.

@echo off
rem Script written by BrendanSilva [bl8086]
rem You need DEBUG.EXE executable in your system.
setlocal enabledelayedexpansion
set /a _er=0
set /a _n=0
set _ln=%~4
goto init
:howuse ------------------------------------------------------------------------
    echo.
    echo ECOL.BAT - v2.0
    echo Print colored text as batch script without temporary files.
    echo Written by bl8086
    echo.
    echo Syntax:
    echo ECOL.BAT [COLOR] [X] [Y] "Insert your text here"
    echo COLOR value must be a hexadecimal number like "color /?" information
    echo.
    echo Example:
    echo ECOL.BAT F0 20 30 "640K ought to be enough for anybody."
    echo.
    goto :eof
:error ------------------------------------------------------------------------
    set /a "_er=_er | (%~1)"
    goto :eof
:gcnvhx ------------------------------------------------------------------------
    set _cvhx=
    set /a _cvint=%~1
:cnvhx
    set /a "_gch = _cvint & 0xF"
    set _cvhx=!nsys:~%_gch%,1!%_cvhx%
    set /a "_cvint = _cvint >> 4"
    if !_cvint! neq 0 goto cnvhx
    goto :eof
:init --------------------------------------------------------------------------
    if "%~4"=="" call :error 0xff
    (
        set /a _cl=0x%1
        call :error !errorlevel!
        set _cl=%1
        call :error "0x!_cl! ^>^> 8"
        set /a _px=%2
        call :error !errorlevel!
        set /a _py=%3
        call :error !errorlevel!
    ) 2>nul 1>&2
    if !_er! neq 0 (
        echo.
        echo ERROR: value exception "!_er!" occurred. Check memory out.
        echo.
        goto howuse
    )
    set nsys=0123456789ABCDEF
    set /a cnb=0
    set /a cnl=0
    set _cvhx=0
    set _cvint=0
    set _cvmhx=0
:parse -------------------------------------------------------------------------
    set _ch=!_ln:~%_n%,1!
    if "%_ch%"=="" goto perform
    set /a "cnb += 1"
    if %cnb% gtr 7 (
        set /a cnb=0
        set /a "cnl += 1"
    )
    set bln%cnl%=!bln%cnl%! "!_ch!" %_cl%
    set /a "_n += 1"
    goto parse
:perform -----------------------------------------------------------------------
    set /a "in = ((_py * 0xA0) + (_px << 1)) & 0xFFFF"
    call :gcnvhx %in%
    set ntr=!_cvhx!
    set /a jmp=0xe
    set bl8086str=echo.h 0 0
    @for /l %%x in (0,1,%cnl%) do (
        set bl8086str=!bl8086str!^&echo.eb800:!ntr! !bln%%x!
        set /a "in=!jmp! + 0x!ntr!"
        call :gcnvhx !in!
        set ntr=!_cvhx!
        set /a jmp=0x10
    )
    (
    echo %bl8086str%
    echo.q
    ) |debug >nul 2>&1

This script can write your text in any screen position. Also using any color.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
LucasLyn-X
  • 11
  • 1
0

For windows 10, there's a much simpler option for color handling when displaying text with a variety of colors on the same screen.

Virtual terminal sequences allow contol over color, cursor position and properties and the screen buffer.

The below macro simplifies their repeated use.

::: Author T3RRY : Created 09/04/2021 : Version 1.0.1
:::
::: Purpose      : Color and cursor position macro for windows 10 batch files
::: - Allows rapid display of colored output at specified screen position.
:::   For more information, read the usage.
:::
::: Uses macro parameter and switch handling template.
:::  - See :  https://pastebin.com/gzL7AYpC

@Echo off

:# Windows Version control. Assigns flag true if system is windows 10.
 Set "Win10="
 Ver | Findstr /LIC:" 10." > nul && Set "Win10=true"

:# Test if virtual terminal codes enabled ; enable if false
:# removes win10 flag definition if version does not support Virtual Terminal sequences
 If defined Win10 (
  Reg Query HKCU\Console | %SystemRoot%\System32\findstr.exe /LIC:"VirtualTerminalLevel    REG_DWORD    0x1" > nul || (
    Reg Add HKCU\Console /f /v VirtualTerminalLevel /t REG_DWORD /d 1
  ) > Nul || Set "Win10="
 )
 If not defined Win10 (
  Echo(Virtual terminal sequences not supported on your system
  Exit /B 1
 )

 If "%~1" == "" (
  Mode 160,30
  Cls
 )
(Set \n=^^^

%= \n macro newline variable. Do not modify =%)

:# assign virtual terminal control character 0x27 'escape' variable \E
 For /F %%a in ( 'Echo prompt $E ^| cmd' )Do Set "\E=%%a"

:# Virtual Terminal 'VT' sequence Resource : 
:# https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences

::# usage: %$Cout% /Switch /Switch value
::# -----------------------------------------------------------------------------------------------------
::# Available Switches     : Description:
::# -----------------------------------------------------------------------------------------------------
::# /Help                  : This help screen
::# /S String              : String to be output
::# /S String{FS}          : '/' must be substituted with {FS}
::# /C Integer             : Declare output color using VT sequence
::# /C Integer,Integer     : Chain   mulitple VT color sequences
::# /C Integer;Integer     : Combine multiple VT values into the one sequence
::# /Y Integer             : Move cursor to Line Integer   [ absolute ]
::# /X Integer             : Move cursor to Column Integer [ absolute ]
::# /U Integer             : Move cursor Up by Integer
::# /D Integer             : Move cursor Down by Integer
::# /R Integer             : Move cursor Right by Integer
::# /L Integer             : Move cursor Left by Integer
::# /H -                   : Hide the cursor
::# /H +                   : Show the cursor
::# /Alt                   : Switch to alternate   buffer [ main buffer is preserved ]
::# /Main                  : Return to main screen buffer [ alternate buffer is cleared ]
::# /K                     : Clears text to right of current cursor position
::# /Z Integer             : Deletes Integer columns right of the cursor, shifting existing text left
::# /I Integer             : Inserts whitespace into Integer columns right of Cursor, shifting text right
::# /N                     : Output a newline after other switches are executed.
::# -----------------------------------------------------------------------------------------------------
::#

:# Note on macro switch handling: Switches must not share a common prefix.
:#  - IE:
:#   - Invalid: D and DE [ switch assessment method would assign D as true if /DE used ]
:#   - Valid  : DA and DE
 Set Switches= "help" "S" "C" "Y" "X" "U" "D" "R" "L" "H" "Alt" "Main" "K" "Z" "I" "N"

 Set $Cout=For %%n in (1 2)Do if %%n==2 (%\n%
  For %%G in ( %Switches% )Do set "Switch[%%~G]="%\n%
  Set "leading.args=!args:*/=!"%\n%
  For /F "Delims=" %%G in ("!leading.args!")Do Set "leading.args=!args:/%%G=!"%\n%
  Set ^"args=!args:"=!" %\n%
  Set "i.arg=0"%\n%
  For %%G in (!leading.args!)Do (%\n%
   Set /A "i.arg+=1"%\n%
   Set "arg[!i.arg!]=%%~G"%\n%
  )%\n%
  For %%G in ( %Switches% )Do If not "!args:/%%~G=!" == "!args!" (%\n%
   If not "!args:/%%~G: =!" == "!args!" Set "args=!args:/%%~G: =/%%~G !"%\n%
   If not "!args:/%%~G =!" == "!args!" Set "switch[%%~G]=!Args:*/%%~G =!"%\n%
   If not "!args:/%%~G:=!" == "!args!" Set "switch[%%~G]=!Args:*/%%~G:=!"%\n%
   If not "!switch[%%~G]:*/=!" == "!switch[%%~G]!" (%\n%
    Set "Trail[%%~G]=!switch[%%~G]:*/=!"%\n%
    For %%v in ("!Trail[%%~G]!")Do (%\n%
     Set "switch[%%~G]=!switch[%%~G]: /%%~v=!"%\n%
     Set "switch[%%~G]=!switch[%%~G]:/%%~v=!"%\n%
    )%\n%
    Set "Trail[%%~G]="%\n%
    If "!switch[%%~G]:~-1!" == " " Set "switch[%%~G]=!switch[%%~G]:~0,-1!"%\n%
    If "!switch[%%~G]!" == "" Set "switch[%%~G]=true"%\n%
   )%\n%
  )%\n%
  If "!Switch[help]!" == "true" (For /F "Tokens=1,2 Delims=#" %%Y in ('findstr /BLIC:"::#" "%~f0"')Do Echo(%%Z) ^& Timeout /T 1 /Nobreak ^> nul ^& Pause%\n%
  If not "!Switch[C]!" == ""    (Set "_Color_=%\E%[!Switch[C]:,=m%\E%[!m")Else Set "_Color_="%\n%
  If not "!Switch[Y]!" == ""    (Set "_Ypos_=%\E%[!Switch[Y]!d")Else Set "_Ypos_="%\n%
  If not "!Switch[X]!" == ""    (Set "_Xpos_=%\E%[!Switch[X]!G")Else Set "_Xpos_="%\n%
  If not "!Switch[U]!" == ""    (Set "_Yoffset_=%\E%[!Switch[U]!A")Else Set "_Yoffset_="%\n%
  If not "!Switch[D]!" == ""    Set "_Yoffset_=%\E%[!Switch[D]!B"%\n%
  If not "!Switch[R]!" == ""    (Set "_Xoffset_=%\E%[!Switch[R]!C")Else Set "_Xoffset_="%\n%
  If not "!Switch[L]!" == ""    Set "_Xoffset_=%\E%[!Switch[L]!D"%\n%
  If "!Switch[H]!" == "-"       Set "_Cursor_=%\E%[?25l"%\n%
  If "!Switch[H]!" == "+"       Set "_Cursor_=%\E%[?25h"%\n%
  If "!Switch[Main]!" == "true" (Set "_Buffer_=%\E%[?1049l")Else Set "_Buffer_="%\n%
  If "!Switch[Alt]!" == "true"  Set "_Buffer_=%\E%[?1049h"%\n%
  If not "!Switch[K]!" == ""    (Set "_LineClear_=%\E%[K")Else Set "_LineClear_="%\n%
  If not "!Switch[Z]!" == ""    (Set "_Delete_=%\E%[!Switch[Z]!P")Else Set "_Delete_="%\n%
  If not "!Switch[I]!" == ""    (Set "_Insert_=%\E%[!Switch[I]!@")Else Set "_Insert_="%\n%
  ^< nul set /P "=!_Buffer_!!_Cursor_!!_Ypos_!!_YOffset_!!_Xpos_!!_XOffset_!!_Delete_!!_Insert_!!_Color_!!_LineClear_!!Switch[S]:{FS}=/!%\E%[0m"%\n%
  If "!Switch[N]!" == "true"    Echo(%\n%
 ) Else Set args=

:# enable macro
Setlocal EnableExtensions EnableDelayedExpansion

:# facilitate testing of the macro using parameters from the command line
 if not "%~1" == ""  (
  %$Cout% %*
  Exit /B !Errorlevel!
 )

:# usage examples
 %$Cout% /help

 CLS
 %$Cout% /C 31,4,48;2;0;120;180 /S hello world /Y 5 /X 10 /H -
timeout /t 1 > nul
 %$Cout% /Alt /C 32 /Y 6 /X 16 /S Goodbye
timeout /t 1 > nul
 %$Cout% /Main /L 6 /I 28 /C 7,1,90,48;2;150;0;170 /S " again my friends around the "
timeout /t 1 > nul

:# Animation examples

 For /L %%i in (1 1 34)Do (
  For /L %%. in (1 1 20)Do Call :_FalseLabel_Delay 2> nul
  %$Cout% /X 10 /Z 1
 )
 For %%L in (G o o d b y e " ")Do (
  For /L %%. in (1 1 40)Do Call :_FalseLabel_Delay 2> nul
  %$Cout% /I 1 /C 31 /S "%%~L"
 )
 %$Cout% /H + /N
 pause
Goto :eof
T3RR0R
  • 2,747
  • 3
  • 10
  • 25
0

A fast alternative to color efficiently with cmd batch since Windows XP by using PowerShell as a subprocess linked to the console output through a named pipe. It can be done with FindStr too, but PowerShell offers more options and seems quicker.

The main interest in keeping PowerShell as a subprocess, using a pipe to communicate, is that the display is far more faster than launching PowerShell or FindStr for each line to display.

Other good points :

  • No need for temporary files
  • Echoing though a pipe permits the display of the full ASCII table without bothering escapes.
  • Works fine with fd redirection. To color only stderr as example, or to redirect to a file / other process.

Here is a sample code for doing that :

::
:: Launch a PowerShell child process in the background linked to the console and 
:: earing through named pipe PowerShellCon_%PID%
::
:: Parameters :
::   [ PID ] : Console Process ID used as an identifier for the named pipe, launcher PID by default.
::   [ timeout ] : Subprocess max life in seconds, 300 by default. If -1, the subprocess
::                  will not terminate while the process %PID% is still alive.
:: Return :
::   0 if the child PowerShell has been successfully launched and the named pipe is available.
::   1 if it fails.
::   2 if we can't get a PID.
::   3 if PowerShell is not present or doesn't work.
::
:LaunchPowerShellSubProcess
  SET LOCALV_PID=
  SET LOCALV_TIMEOUT=300
  IF NOT "%~1" == "" SET LOCALV_PID=%~1
  IF NOT "%~2" == "" SET LOCALV_TIMEOUT=%~2
  powershell -command "$_" 2>&1 >NUL
  IF NOT "!ERRORLEVEL!" == "0" EXIT /B 3
  IF "!LOCALV_PID!" == "" (
    FOR /F %%P IN ('powershell -command "$parentId=(Get-WmiObject Win32_Process -Filter ProcessId=$PID).ParentProcessId; write-host (Get-WmiObject Win32_Process -Filter ProcessId=$parentId).ParentProcessId;"') DO (
      SET LOCALV_PID=%%P
    )
  )
  IF "!LOCALV_PID!" == "" EXIT /B 2
  START /B powershell -command "$cmdPID=$PID; Start-Job -ArgumentList $cmdPID -ScriptBlock { $ProcessActive = $true; $timeout=!LOCALV_TIMEOUT!; while((!LOCALV_TIMEOUT! -eq -1 -or $timeout -gt 0) -and $ProcessActive) { Start-Sleep -s 1; $timeout-=1; $ProcessActive = Get-Process -id !LOCALV_PID! -ErrorAction SilentlyContinue; } if ($timeout -eq 0 -or ^! $ProcessActive) { Stop-Process -Id $args; } } | Out-Null ; $npipeServer = new-object System.IO.Pipes.NamedPipeServerStream('PowerShellCon_!LOCALV_PID!', [System.IO.Pipes.PipeDirection]::In); Try { $npipeServer.WaitForConnection(); $pipeReader = new-object System.IO.StreamReader($npipeServer); while(($msg = $pipeReader.ReadLine()) -notmatch 'QUIT') { $disp='write-host '+$msg+';'; invoke-expression($disp); $npipeServer.Disconnect(); $npipeServer.WaitForConnection(); }; } Finally { $npipeServer.Dispose(); }" 2>NUL
  SET /A LOCALV_TRY=20 >NUL
  :LaunchPowerShellSubProcess_WaitForPipe
  powershell -nop -c "& {sleep -m 50}"
  SET /A LOCALV_TRY=!LOCALV_TRY! - 1 >NUL
  IF NOT "!LOCALV_TRY!" == "0" cmd /C "ECHO -NoNewLine|MORE 1>\\.\pipe\PowerShellCon_!LOCALV_PID!" 2>NUL || GOTO:LaunchPowerShellSubProcess_WaitForPipe
  IF "!LOCALV_TRY!" == "0" EXIT /B 1
  EXIT /B 0

This "code" is written with delayed expansion ON but can be rewrite to work without it. There is many security points to consider, do not use it directly in the wild.

How to use it :

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
IF ERRORLEVEL 1 (
  ECHO Extension inapplicable
  EXIT /B 1
)
::
SETLOCAL ENABLEDELAYEDEXPANSION
IF ERRORLEVEL 1 (
  ECHO Expansion inapplicable
  EXIT /B 1
)
CALL:LaunchPowerShellSubProcess
IF NOT ERRORLEVEL 0 EXIT /B 1
CALL:Color Cyan "I write this in Cyan"
CALL:Blue "I write this in Blue"
CALL:Green "And this in green"
CALL:Red -nonewline "And mix Red"
CALL:Yellow "with Yellow"
CALL:Green "And not need to trouble with ()<>&|;,%""^ and so on..."
EXIT /B 0
:Color
ECHO -foregroundcolor %*>\\.\pipe\PowerShellCon_!LOCALV_PID!
ECHO[|SET /P=>NUL
GOTO:EOF
:Blue
ECHO -foregroundcolor Blue %*>\\.\pipe\PowerShellCon_!LOCALV_PID!
ECHO[|SET /P=>NUL
GOTO:EOF
:Green
ECHO -foregroundcolor Green %*>\\.\pipe\PowerShellCon_!LOCALV_PID!
ECHO[|SET /P=>NUL
GOTO:EOF
:Red
ECHO -foregroundcolor Red %*>\\.\pipe\PowerShellCon_!LOCALV_PID!
ECHO[|SET /P=>NUL
GOTO:EOF
:Yellow
ECHO -foregroundcolor Yellow %*>\\.\pipe\PowerShellCon_!LOCALV_PID!
ECHO[|SET /P=>NUL
GOTO:EOF
::
:: Launch a PowerShell child process in the background linked to the console and 
:: earing through named pipe PowerShellCon_%PID%
::
:: Parameters :
::   [ PID ] : Console Process ID used as an identifier for the named pipe, launcher PID by default.
::   [ timeout ] : Subprocess max life in seconds, 300 by default. If -1, the subprocess
::                  will not terminate while the process %PID% is still alive.
:: Return :
::   0 if the child PowerShell has been successfully launched and the named pipe is available.
::   1 if it fails.
::   2 if we can't get a PID.
::   3 if PowerShell is not present or doesn't work.
::
:LaunchPowerShellSubProcess
  SET LOCALV_PID=
  SET LOCALV_TIMEOUT=300
  IF NOT "%~1" == "" SET LOCALV_PID=%~1
  IF NOT "%~2" == "" SET LOCALV_TIMEOUT=%~2
  powershell -command "$_" 2>&1 >NUL
  IF NOT "!ERRORLEVEL!" == "0" EXIT /B 3
  IF "!LOCALV_PID!" == "" (
    FOR /F %%P IN ('powershell -command "$parentId=(Get-WmiObject Win32_Process -Filter ProcessId=$PID).ParentProcessId; write-host (Get-WmiObject Win32_Process -Filter ProcessId=$parentId).ParentProcessId;"') DO (
      SET LOCALV_PID=%%P
    )
  )
  IF "!LOCALV_PID!" == "" EXIT /B 2
  START /B powershell -command "$cmdPID=$PID; Start-Job -ArgumentList $cmdPID -ScriptBlock { $ProcessActive = $true; $timeout=!LOCALV_TIMEOUT!; while((!LOCALV_TIMEOUT! -eq -1 -or $timeout -gt 0) -and $ProcessActive) { Start-Sleep -s 1; $timeout-=1; $ProcessActive = Get-Process -id !LOCALV_PID! -ErrorAction SilentlyContinue; } if ($timeout -eq 0 -or ^! $ProcessActive) { Stop-Process -Id $args; } } | Out-Null ; $npipeServer = new-object System.IO.Pipes.NamedPipeServerStream('PowerShellCon_!LOCALV_PID!', [System.IO.Pipes.PipeDirection]::In); Try { $npipeServer.WaitForConnection(); $pipeReader = new-object System.IO.StreamReader($npipeServer); while(($msg = $pipeReader.ReadLine()) -notmatch 'QUIT') { $disp='write-host '+$msg+';'; invoke-expression($disp); $npipeServer.Disconnect(); $npipeServer.WaitForConnection(); }; } Finally { $npipeServer.Dispose(); }" 2>NUL
  SET /A LOCALV_TRY=20 >NUL
  :LaunchPowerShellSubProcess_WaitForPipe
  powershell -nop -c "& {sleep -m 50}"
  SET /A LOCALV_TRY=!LOCALV_TRY! - 1 >NUL
  IF NOT "!LOCALV_TRY!" == "0" cmd /C "ECHO -NoNewLine|MORE 1>\\.\pipe\PowerShellCon_!LOCALV_PID!" 2>NUL || GOTO:LaunchPowerShellSubProcess_WaitForPipe
  IF "!LOCALV_TRY!" == "0" EXIT /B 1
  EXIT /B 0

Link to my original answer on the same topic.

Zilog80
  • 2,534
  • 2
  • 15
  • 20
-1

Just need of COLORMSG :

@echo off
(
echo e100
echo 0F B6 0E 80 00 E3 4F BF 81 00 B0 20 FC F3 AE 74
echo e110
echo 45 E3 43 8A 45 FF E8 43 00 80 3D 20 74 0E C0 E0
echo e120
echo 04 8A E0 8A 05 E8 34 00 0A C4 47 49 E3 28 32 E4
echo e130
echo 50 B0 22 F2 AE 75 1F E3 1D 8B F7 8B D1 F2 AE 75
echo e140
echo 01 41 2B D1 74 10 8B CA 5B B0 20 B4 09 CD 10 AC
echo e150
echo B4 0E CD 10 E2 F9 32 C0 B4 4C CD 21 3C 61 72 02
echo e160
echo 2C 20 3C 41 72 02 2C 07 2C 30 C3
echo rcx
echo 6b
echo w
echo q
) | debug colormsg.com > nul
ren colormsg.com colormsg.exe

Use the colors cmd default :

color /?

Use echo. to break line

Use spacebar to move msg horizontally

Example:

@echo off

REM one color in line
echo.
colormsg a "green color"

REM break line
echo.

REM two colors in line
colormsg b "color blue"
colormsg c "color red"

REM break line
echo.

REM two colors with spacing horizontally (no use tab)
colormsg d "purple color"
colormsg e "             yellow color"
echo.
echo.
pause

that's all !!

Gohan
  • 7
  • 2