ansi.bat: handling ANSI sequences in nowadays WIN terminals
Posted: 24 Nov 2020 07:00
A few days ago i found out that windows terminal supports ANSI sequences.
It's cool, of course. However, as it's usual in Windows world, every cool feature has worse things. VT is off, by default. Luckily, it can be turned on easily.
ENABLE/DISABLE
Turn on:
Turn off:
EXAMPLES
Independently on this feature is turned on or off, both type and echo support ANSI:
Powershell supports as well:
Other commands do not support.
ENHANCEMENT AND TOOL
In Unix it's allowed to specify escape sequences in the forms:
Windows doesn't support them. To enable them in Windows as well, I developed a simple script (hybrid of CMD+WSF+JS). It works as a pipe filter, capturing input and transforming into ANSI printable sequences.
The script has a comprehensive description and couple of examples.
It checks if VT setting enabled and prints result directly, otherwise emulate via echo.
SOURCE CODE
It's cool, of course. However, as it's usual in Windows world, every cool feature has worse things. VT is off, by default. Luckily, it can be turned on easily.
ENABLE/DISABLE
Turn on:
Code: Select all
reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1
Code: Select all
reg delete HKCU\Console /v VirtualTerminalLevel
Independently on this feature is turned on or off, both type and echo support ANSI:
Code: Select all
for /f %a in ('echo prompt $e^| cmd') do @set "ESC=%a"
echo %ESC%[107m %ESC%[0m & echo %ESC%[104m %ESC%[0m & echo %ESC%[101m %ESC%[0m
Code: Select all
for /f %a in ('echo prompt $e^| cmd') do @set "ESC=%a"
echo %ESC%[107m %ESC%[0m >> example.txt
echo %ESC%[104m %ESC%[0m >> example.txt
echo %ESC%[101m %ESC%[0m >> example.txt
type example.txt
Code: Select all
$ESC = [char]27
"$ESC[107m $ESC[0m`n$ESC[104m $ESC[0m`n$ESC[101m $ESC[0m"
ENHANCEMENT AND TOOL
In Unix it's allowed to specify escape sequences in the forms:
Code: Select all
"\e[" <options> <command>
"\033[" <options> <command>
"\x1b[" <options> <command>
"^[[" <options> <command> # in Windows "^^^^[[" is required to print
The script has a comprehensive description and couple of examples.
It checks if VT setting enabled and prints result directly, otherwise emulate via echo.
SOURCE CODE
Code: Select all
<?xml :
: version="1.0" encoding="utf-8" ?>
<!-- :
@echo off
if "%~1" == "" timeout /t 0 1>nul 2>&1 && (
cscript //nologo "%~f0?.wsf" /?
goto :EOF
)
reg query "HKCU\Console" /v VirtualTerminalLevel 2>nul | find "0x1" >nul && (
cscript //nologo "%~f0?.wsf" %*
goto :EOF
)
for /f "tokens=* delims=" %%s in ( '
cscript //nologo "%~f0?.wsf" %*
' ) do echo:%%s
goto :EOF
: -->
<package>
<job id="ansi">
<?job error="false" debug="false" ?>
<script language="javascript"><![CDATA[
var NAME = 'ANSI';
var VERSION = '0.0.1';
]]></script>
<runtime>
<description><![CDATA[
Parse the specified text from the input file or pipe and output it
accordingly the ANSI codes provided within the text.
Escaping
Interpret the following escaped characters:
\a Bell
\b Backspace
\e Escape character
\f Form feed
\n New line
\r Carriage return
\t Horizontal tabulation
\v Vertical tabulation
\\ Backslash
\0nnn The character by its ASCII code (octal)
\xHH The character by its ASCII code (hexadecimal)
ANSI sequences
<ESC> [ <list> <code>
<ESC> Escape character in the form "\e", "\033", "\x1B", "^["
<list> The list of numeric codes
<code> The sequence code
Moves the cursor n (default 1) cells in the given direction. If the cursor
is already at the edge of the screen, this has no effect.
\e[nA Cursor Up
\e[nB Cursor Down
\e[nC Cursor Forward
\e[nD Cursor Back
Moves cursor to beginning of the line n (default 1).
\e[nE Cursor Next Line
\e[nF Cursor Previous Line
Cursor position
\e[nG Moves the cursor to column n.
\e[n;mH Moves the cursor to row n, column m.
\e[n;mf The same as above.
Erasing
\e[nJ Clears part of the screen. If n is 0 (or missing), clear from
cursor to end of screen. If n is 1, clear from cursor to
beginning of the screen. If n is 2, clear entire screen.
\e[nK Erases part of the line. If n is zero (or missing), clear from
cursor to the end of the line. If n is one, clear from cursor
to beginning of the line. If n is two, clear entire line.
Cursor position does not change.
Colorizing
\e[n1[;n2;...]m, where n's are as follows:
0 All attributes off
1 Increase intensity
2 Faint (decreased intensity)
3 Italic (not widely supported)
4 Underline
5 Slow blink
6 Rapid blink
7 Reverse (invert the foreground and background colors)
8-29 Rarely supported
30-37 Set foreground color (30+x, where x from the tables below)
38 Set foreground color (Next arguments are 5;n or 2;r;g;b)
39 Default foreground text color
40-47 Set background color (40+x)
48 Set foreground color (Next arguments are 5;n or 2;r;g;b)
49 Default background color
50-74 Rarely supported
90-97 Set foreground color, high intensity (90+x)
100-107 Set background color, high intensity (100+x)
ANSI colors (default usage)
Intensity 0 1 2 3 4 5 6 7
Normal Black Red Green Yellow Blue Magenta Cyan White
Bright Black Red Green Yellow Blue Magenta Cyan White
References
http://en.wikipedia.org/wiki/ANSI_escape_code
http://misc.flogisoft.com/bash/tip_colors_and_formatting
http://stackoverflow.com/a/24273024/3627676
http://www.robvanderwoude.com/ansi.php#AnsiArt
]]></description>
<example><![CDATA[
Examples:
- Print data from pipe:
echo \e[107m \e[0m\n\e[104m \e[0m\n\e[101m \e[0m | ansi
- Prepare data in the file and print:
del example.txt
echo \e[107m \e[0m>>example.txt
echo \e[104m \e[0m>>example.txt
echo \e[101m \e[0m>>example.txt
ansi < example.txt
]]></example>
<named
name="no-eol"
helpstring="Skip explicit end of line (skip explicit CRLF)"
type="simple"
required="false"
/>
<named
name="no-space"
helpstring="Skip the last trailing space printed by echo in DOS sessions"
type="simple"
required="false"
/>
<named
name="safe"
helpstring="Reset all color attributes to defaults at the end of execution"
type="simple"
required="false"
/>
</runtime>
<!-- <script language="javascript" src="./ansi.parse.js"></script> -->
<script language="javascript"><![CDATA[
function ansi_parse(text, options) {
options = options || {};
if ( options.safe ) {
text += '\\e[0m';
}
var chars = {
'a': String.fromCharCode(7),
'b': '\b',
'e': String.fromCharCode(27),
'f': '\f',
'n': '\n',
'r': '\r',
't': '\t',
'v': '\v',
'\\': '\\'
};
var re_src = [
'(?:\\\\(' + '[abefnrtv\\\\]' + '))' // Escaped chars above
, '|'
, '(?:\\\\(' + '0[0-7]{1,3}' + '))' // ASCII code (oct)
, '|'
, '(?:\\\\(' + 'x[0-9a-fA-F]{1,2}' + '))' // ASCII code (hex)
, '|'
, '(' + '\\^\\[' + ')' // ^[ stands for <ESC>
].join('');
if ( options.no_eol && options.no_space ) {
re_src += '|[ ]\\r?\\n';
} else if ( options.no_eol ) {
re_src += '|\\r?\\n';
} else if ( options.no_space ) {
re_src += '|[ ](?=\\r?\\n)';
}
var re = new RegExp(re_src, 'g');
return text.replace(re, function($0, $1, $2, $3, $4) {
return $1 ? chars[$1] :
$2 ? String.fromCharCode(parseInt($2)) :
$3 ? String.fromCharCode(parseInt('0' + $3)) :
$4 ? chars.e : '';
});
}
]]></script>
<!-- <script language="javascript" src="./ansi.main.js"></script> -->
<script language="javascript"><![CDATA[
var opts = WScript.Arguments.Named;
var text = WScript.StdIn.ReadAll();
var text = ansi_parse(text, {
no_eol: opts.Exists('no-eol'),
no_space: opts.Exists('no-space'),
safe: opts.Exists('safe')
});
WScript.StdOut.Write(text);
]]></script>
</job>
</package>