Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Who wants 3 ways of printing coloured text in a batch WITHOUT any external programs? here they are

561 views
Skip to first unread message

Pedro M

unread,
Jan 21, 2015, 3:56:39 PM1/21/15
to
I present you with 3 subroutines for getting coloured text and/or background in a batch file, without any special EXE. They all use the colour codes from the command «color»
These subroutines weren't created by me, I just found them on the net, and are provided "as is", I take no responsability id your computer explodes, or starts to call you Dave in a monotone voice.

They all have it's advantages and disadvantages, weakness and strenghts, you just choose accordingly with your needs. I hope you enjoy them.


Let's start with the 1st:
("::" is the same as "REM")

ADVANTAGES: it prints almost any char, easy to implement in a batch
DISADVANTAGES: it's outdated, it doesn't work from W7 on (works great on XP though), doesn't need external EXEs installed *BUT* it creates it's own .COM for each colour combination and then executes it. The more colors you use, the more .com files it creates (in your system %temp% folder)

The .COM are created with the "debug" command that is present in XP, and which W7 doesn't have anymore (hence it doesn't work)
The code is written in assembly and then compiled by debug (assembly in a batch.. awsome I know) and finally executed to show the colour

To try it you just have to call the subroutine with the colour code as the 1st argument and the text as the 2nd.

For instance you write in a batch «Hello there» in red text and white background, you do this:
CALL :SEVERAL_COLORS FC Hello there
...and that's it


****************** THE 1ST SUBROUTINE STARTS HERE:

::_________________________________THIS IS THE SUBROUTINE_____________________________
:SEVERAL_COLORS

REM ·····------=======############### ################=======------·····
REM ·····------=======############# #############=======------·····
REM ·····------=======######### OUTDATED! IT DOESN'T WORK ON WINDOWS 7 #########=======------·····
REM ········-------====#### 'debug' ONLY PRESENT AT XP ####====-------········

REM @echo off
:echo col txt -- echoes text in a specific color
:: -- col [in] - color code, append a DOT to omit line break, call 'color /?' for color codes
:: -- txt [in] - text output
:$created 20060101 :$changed 20080219 :$categories Echo,Color
:$source http://www.dostips.com
SETLOCAL
for /f "tokens=1,*" %%a in ("%*") do (
set col=%%a
set txt=%%~b
)
set cr=Y
if "%col:~-1%"=="." (
set cr=N
set col=%col:~0,-1%
)
rem call:getColorCode "%col%" col
set "com=%temp%\color%col%.com"
if not exist "%com%" (
echo.N %COM%
echo.A 100
echo.MOV BL,%col%
echo.MOV BH,0
echo.MOV SI,0082
echo.MOV AL,[SI]
echo.MOV CX,1
echo.MOV AH,09
echo.INT 10
echo.MOV AH,3
echo.INT 10
echo.INC DL
echo.MOV AH,2
echo.INT 10
echo.INC SI
echo.MOV AL,[SI]
echo.CMP AL,0D
echo.JNZ 109
echo.RET
echo.
echo.r cx
echo.22
echo.w
echo.q
)|debug>NUL
"%com%" %txt%
if "%cr%"=="Y" echo.
EXIT /b
GOTO:EOF
::_____________________________IT ENDS HERE___________________________








----
-----------
----------------
-------------------> NOW FOR THE 2ND SUBROUTINE:

This method uses the only (or one of the only) command present in almost all Microsoft OSes that can print colours: FINDSTR, and then uses some voodoo and batch kung-fu to harness that colour output to our needs :)

First you need a «setlocal EnableDelayedExpansion» before using it; next before you call the main colour subroutine, you need to initialize another subroutine: «InitColourPrint», that will create a 1 byte file called 'x' in your temp folder (it's necessary). This subroutine only needs to be called once in the Batch.
Then you can finally print some colours by calling the main subroutine each time you want some colour goodness :)

Example:

@echo off
setlocal EnableDelayedExpansion
CALL :InitColourPrint

:: this will print in light blue/dark background a sentence
call :COR_AVANC 0b "this 2nd subroutine is awsome!"
echo.
:: ...now in blue text, dark blue background
call :COR_AVANC 0b "it sure is :)"


At the end of the batch you will undo the setlocal by putting «endlocal»; and if you want to delete the 'x' file, apply this at the end of the batch: del "%temp%\x"


ADVANTAGES: works in XP *AND* W7
DISADVANTAGES: slightly more complex to implement.
It won't make carriage returns, you'll have to write "echo." for those
If the coloured string comes too close to the end of the line (4 or 5 chars left) it writes some '.' and '/' after the string. However if the string just fills the whole line it doesn't write them anymore, and if the string lenght exceeds the line size it'll just write the rest on the next line.
The string *CAN'T* begin with a / or \, otherwise it'll give an error. It *can* end with / or \, *BUT* it can't have any other char after it (besides the ending quotation mark)
EX: all of these will give errors:
call :COR_AVANC 0b "\Damn, an error"
call :COR_AVANC 0b "/ oh no another one"
call :COR_AVANC 0b "and another/a"
call :COR_AVANC 0b "...yep, one more error\bbb"



************ And now for the "InitColourPrint" and "COR_AVANC" subroutines:


:: =====================================
:InitColourPrint
:: @@@@ INITIALIZE THE MAIN ROUTINE "COR_AVANC"
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do set "DEL=%%a"
:: Prepare a file "X" with only one dot
REM <NUL >"X" set /p ".=."
<NUL >"%temp%\x" set /p ".=."
exit /b
:: =====================================

::############################################--- IT STARTS HERE ---#################################
:: @@@@@@@@@@@@@@@@@@ IMPRIME STRINGS COM VÁRIAS CORES @@@@@@@@@@@@@@@@@@@@
:: @@@@@@ mais limitado c/ certas sequências de caracteres @@@@@@@
:: @@@@@ **e não permite mudanças de linha internas** @@@@@
:: @@@@@@@@@@@@ @@@@@@@@@@@@
:: ººººººººººººººººººººººººººº

:: »»»»»» NECESSITA DE UM 'setlocal EnableDelayedExpansion' LOGO A SEGUIR AO @ECHO OFF NO COMEÇO DO .BAT

:: »»»»»» ...E DE CHAMAR A SUB-ROTINA "InitColourPrint" **ANTES** DE CHAMAR QUALQUER COR
:: »»»»»» (SÓ PRECISA SER FEITA 1 VEZ NO .BAT)


:: »»»»»» EXEMPLOS:
:: call :COR_AVANC 1a "a"
:: call :COR_AVANC 1b "b lalala"
:: call :COR_AVANC 1c "^!<>&| %%%%"*?:\/"
::::::::::::::::: COMEÇA AQUI ::::::::::::::::::::::

:COR_AVANC
set "param=^%~2" !
set "param=!param:"=\"!"
pushd "%temp%"
findstr /P /A:%1 "." "!param!\..\X" nul
<NUL set /p ".=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
popd
exit /b
::##################################################################################






-----
-----------
----------------
-------------------> FINALLY, THE 3RD SUBROUTINE:

ADVANTAGES: No limits of chars, and carriage returns are now an option. Also no more limitations with the '\' and '/' like the previous one.
DISADVANTAGES: still it will only print what you see on your keyboard plus the accentuated chars, it will *NOT* print those fancy extended ASCII symbols (those in the right half of this table: http://www.asciitable.com/index/extend.gif). Only the 1st outdated subroutine can print those.
If the coloured string comes too close to the right border (3 or 4 chars left) it writes some '.' and '/' after the string. However it it just fills the whole line it doesn't write them, and if the string lenght exceeds the line size it'll just write the rest on the next line.

It works the same way the previous one worked, but this time the subroutines are "InitColourPrint_2" and "COR_AVANC_2"

Ex:
@echo off
setlocal EnableDelayedExpansion
CALL :InitColourPrint_2

call :COR_AVANC_2 E1 "this one is even better"
echo.
call :COR_AVANC_2 C7 " indeed, it even has carriage included" /n

...the "/n" is the optional carriage return, like if I added an "echo." after it.

It can also print e.g. the variable MY_TEXT:
CALL :COR_AVANC_2 0D My_TEXT
...note that in this case the variable doesn't need the '%' around it as it's usual with batches, neither uses quoting marks (") around the text anymore.





***********************At last, the subroutines for the 3rd method are:

:: ============================
:InitColourPrint_2
:: @@@@ INITIALIZE THE MAIN ROUTINE "COR_AVANC_2"
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do set "DEL=%%a"
<NUL >"%temp%\x" set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%.%DEL%"
exit /b
:: ============================

::#################################### BEGINNING ##############################################
:: #### escolher 'COR_AVANC_2' ou 'COR_AVANC_2VAR' ####
:: *********** ***********
:: @@@@@@@@@@@ @@@@@@@@@@@
:: @@@@@@@@@@@@@@@@@@@@@@@@ IMPRIME STRINGS COM VÁRIAS CORES @@@@@@@@@@@@@@@@@@@@@@@@@@
:: ········@@@@@@@@@@ sem limites :) @@@@@@@@@@@········
:: @@@@@@@ @@@@@@@
:: @@@@@@@@@@@@@@@@@@@@

:: »»»»»» NECESSITA DE UM 'setlocal' LOGO A SEGUIR AO @ECHO OFF NO COMEÇO DO .BAT
:: »»»»»» ...E DE UM 'call :initColourPrint_2' **ANTES** DE CHAMAR QUALQUER COR
:: »»»»»» (SÓ PRECISA SER FEITA 1 VEZ NO .BAT)

:: »»»»»» EXEMPLOS:

:: ######## Tanto pode imprimir texto directo (acrescentar /n para mudar de linha)...
:: call :COR_AVANC_2 0a "bláblá"
:: call :COR_AVANC_2 2f "q:" /n

:: ######## ...como através de uma variável:
:: set "txt=^" & call :COR_AVANC_2VAR 0c txt
:: set complex="c:\hello world!/.\..\\a//^<%%>&|!" /^<%%^>^&^|!\
:: set complex="c:\hello world!"
:: call :COR_AVANC_2VAR 74 complex /n

:: »»»»» POR FIM, PARA LIMPAR AS VARIÁVEIS FAZER UM 'call :cleanupColorPrint'
::::::::::::::::::::::::: COMEÇA AQUI ::::::::::::::::::::::::::::::::::

:COR_AVANC_2 Color Str [/n]
setlocal
set "str=%~2"
call :COR_AVANC_2VAR %1 str %3
exit /b

:COR_AVANC_2VAR Color StrVar [/n]
if not defined %~2 exit /b
setlocal enableDelayedExpansion
set "str=a%DEL%!%~2:\=a%DEL%\..\%DEL%%DEL%%DEL%!"
set "str=!str:/=a%DEL%/..\%DEL%%DEL%%DEL%!"
set "str=!str:"=\"!"
pushd "%temp%"
findstr /p /A:%1 "." "!str!\..\x" nul
if /i "%~3"=="/n" echo(
exit /b
::######################################## END #################################################

Pedro M

unread,
Jan 21, 2015, 3:58:30 PM1/21/15
to
I hope you enjoy it. And if you are really up to it, why not improve this code by eliminating it's limitations and share here the results? :) Don't count on me for this though, because this one here is already way out of my league.

Pedro M

unread,
Jan 21, 2015, 4:02:03 PM1/21/15
to
**UPDATE**:
When I said this about the disadvantages fo the 3rd routine:
> DISADVANTAGES: still it will only print what you see on your keyboard plus the accentuated chars, it will *NOT* print those fancy extended
>ASCII symbols (those in the right half of this table: http://www.asciitable.com/index/extend.gif). Only the 1st outdated subroutine can print those.

I was mistaken, I just checked and the 1st subroutine *CAN'T* print those extended characters, or at least most of them.

foxidrive

unread,
Jan 21, 2015, 7:44:14 PM1/21/15
to
Here are two more.



============================================================
@Echo Off
Call :Color A "######" \n E "" C " 22 " E "!" \n B "######" \n
Pause >Nul
Exit /B

:Color
:: v22
:: Arguments: hexColor text [\n] ...
:: \n -> newline ... -> repeat
:: Supported in windows XP, 7, 8.
:: This version works using Cmd /U
:: In XP extended ascii characters are printed as dots.
:: For print quotes, use empty text.
SetLocal EnableExtensions EnableDelayedExpansion
Subst `: "!Temp!" >Nul &`: &Cd \
SetLocal DisableDelayedExpansion
Echo(|(Pause >Nul &Findstr "^" >`)
Cmd /A /D /C Set /P "=." >>` <Nul
For /F %%# In (
'"Prompt $H &For %%_ In (_) Do Rem"') Do (
Cmd /A /D /C Set /P "=%%# %%#" <Nul >`.1
Copy /Y `.1 /B + `.1 /B + `.1 /B `.3 /B >Nul
Copy /Y `.1 /B + `.1 /B + `.3 /B `.5 /B >Nul
Copy /Y `.1 /B + `.1 /B + `.5 /B `.7 /B >Nul
)
:__Color
Set "Text=%~2"
If Not Defined Text (Set Text=^")
SetLocal EnableDelayedExpansion
Set /P "LF=" <` &Set "LF=!LF:~0,1!"
For %%# in ("!LF!") Do For %%_ In (
\ / :) Do Set "Text=!Text:%%_=%%~#%%_%%~#!"
For /F delims^=^ eol^= %%# in ("!Text!") Do (
If #==#! EndLocal
If \==%%# (Findstr /A:%~1 . \` Nul
Type `.3) Else If /==%%# (Findstr /A:%~1 . /.\` Nul
Type `.5) Else (Cmd /A /D /C Echo %%#\..\`>`.dat
Findstr /F:`.dat /A:%~1 .
Type `.7))
If "\n"=="%~3" (Shift
Echo()
Shift
Shift
If ""=="%~1" Del ` `.1 `.3 `.5 `.7 `.dat &Goto :Eof
Goto :__Color


============================================================











============================================================

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal
:: creates a binary which can output colour
:: co1oroutput.bat -s "aa\nbb\n\u0025" ùb 10 ùf 3 ùn ùe


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.BackgroundColor;
var backgroundColor = Console.ForegroundColor;
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 colorSetter( i:Int32 ):ConsoleColor {
switch ( i ) {
case 0:
return ConsoleColor.Black;
break;
case 1:
return ConsoleColor.Blue;
break;
case 2:
return ConsoleColor.Cyan;
break;
case 3:
return ConsoleColor.DarkBlue;
break;
case 4:
return ConsoleColor.DarkCyan;
break;
case 5:
return ConsoleColor.DarkGray;
break;
case 6:
return ConsoleColor.DarkGreen;
break;
case 7:
return ConsoleColor.DarkMagenta;
break;
case 8:
return ConsoleColor.DarkRed;
break;
case 9:
return ConsoleColor.DarkYellow;
break;
case 10:
return ConsoleColor.Gray;
break;
case 11:
return ConsoleColor.Green;
break;
case 12:
return ConsoleColor.Magenta;
break;
case 13:
return ConsoleColor.Red;
break;
case 14:
return ConsoleColor.White;
break;
case 15:
return ConsoleColor.Yellow;
break;
default:
return ConsoleColor.Black;
}

}


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 = colorSetter(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 = colorSetter( backgroundColor );
Console.ForegroundColor = colorSetter( foregroundColor );

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

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

}

Console.BackgroundColor = currentBackground;
Console.ForegroundColor = currentForeground;


============================================================

Pedro M

unread,
Jan 21, 2015, 9:42:10 PM1/21/15
to
On Thursday, January 22, 2015 at 12:44:14 AM UTC, foxidrive wrote:
> Here are two more.
>

Nice, keep them comming.
The second is a little slow, because of java I imagine
Does the 1st method save any file to the disk?

Zaidy036

unread,
Jan 30, 2015, 10:44:00 PM1/30/15
to
:: Tiny Function Color in alt.msdos.batch.nt by cmon...@gmail.com 10/16/12
:: mod to show examples Er...@Bloch.com 06/20/14

@ECHO OFF
For %%b in (0 1 2 3 4 5 6 7 8 9 a b c d e f) DO (
For %%t in (0 1 2 3 4 5 6 7 8 9 a b c d e f) DO (
SET xs=%%b%%%t
CALL :color %%b%%t xs \n
)
pause
)
exit /b

:Color
:: Changes color and background of ECHO
------------------------------------------------------------------
:: v17. Arguments: hexColor variableName [\n]
SetLocal EnableExtensions EnableDelayedExpansion
Subst `: "!Temp!" >Nul &Pushd . &`: &If Not Exist `.bat (
Set /P "=."<Nul >` &For /F "delims=;" %%# in (
'"Prompt;$H;&For %%_ in (1) Do Rem"') Do Echo(Set "b=%%#">`.bat
For %%# in ("(Set n=^" "" ")") Do Echo(%%~#>>`.bat)
Call ` &Set "c=%~1" &Set "t=!%~2!"
If Defined t For %%# in ("!n!") Do For %%_ in (\ / :
) Do Set "t=!t:%%_=%%~#%%_%%~#!"
For /F usebackq^ delims^=^ eol^= %%_ in ('!t:"=\"!') Do (
SetLocal DisableDelayedExpansion
For /F delims^=^ eol^= %%# in ("%%~_") Do If \==%%# (
Findstr /A:%c% "." "\`" Nul &Set /P "=%b%%b%%b%"<Nul
) Else If /==%%# (Findstr /A:%c% "." "/.\`" Nul
Set /P "=%b%%b%%b%%b%%b%"<Nul
) Else (Findstr /A:%c% "." "%%#\..\`" Nul
Set /P "=%b%%b%%b%%b%%b%%b%%b%"<Nul)
EndLocal)
If /I "\n"=="%~3" (Echo()
Popd &Goto :EOF

foxidrive

unread,
Jan 31, 2015, 1:22:22 AM1/31/15
to
On 31/01/2015 14:43, Zaidy036 wrote:
> On 1/21/2015 7:44 PM, foxidrive wrote:
>> Here are two more.
>>
>> :Color
>> :: v22
>> :: Arguments: hexColor text [\n] ...


> :: Tiny Function Color in alt.msdos.batch.nt by cmon...@gmail.com 10/16/12
> :: mod to show examples Er...@Bloch.com 06/20/14

> :Color
> :: Changes color and background of ECHO
> ------------------------------------------------------------------
> :: v17. Arguments: hexColor variableName [\n]

FWIW you will find that the one I posted is by the same fellow and has bug fixes
Some earlier versions didn't run on some OS or had more character limitations etc

see here: http://www.dostips.com/forum/viewtopic.php?p=32630



Zaidy036

unread,
Jan 31, 2015, 11:05:43 AM1/31/15
to
Thanks. I just returned from a trip and did not realize the routine was
still being improved.

0 new messages