3

I'm trying to read a log file with a batch file.

  • If the batch file finds SUCCESS in the log file, the text should be in green.
  • If the batch file finds WARNING in the log file, the text should be in cyan.
  • If the batch file finds ERROR in the log file, the text should be in red.

It works when it finds either of the one value, but if the log file contains two or more of the different results like SUCCESS and WARNING it doesn't work.

Trying to read a log file with batch file on Windows.

@echo off
set LogPath=C:\Mworks\logs
set /p MM-DD="Enter Month and Day (MM-DD) to Search logs for success close eg. 08-24: "
set YEAR=2019
@echo searching for %LogPath%\%YEAR%-%MM-DD%*.log
FOR /F "eol=- tokens=1-8 delims= " %%i in ('find /I "LOG ROTATE COMPLETE" %LogPath%\%YEAR%-%MM-DD%*.log') do set result=%%p
echo %result%

(
IF /I %result%==ERROR (goto :ERROR)
)

(
if /I %result%==SUCCESS (goto :SUCCESS)
)

(
if /I %result%==WARNING (goto :WARNING)
)

REM somehow need to catch value at token 8 and color the lines accordingly
REM proper use of enableDelayedExpansion might help but it's quite tough simply with batch script.
REM I've seen hackoo's version of pinger doing it but the code is hard to understand, which part controls what.
:SUCCESS
color 0A
REM this line needs to be on GREEN
FOR /F "eol=- tokens=1-8 delims= " %%i in ('find /I "LOG ROTATE COMPLETE" %LogPath%\2019-%MM-DD%*.log') do @echo %%i %%j %%k %%l %%m %%n %%o %%p
goto end

:ERROR
color 0C
REM this line nees to be on RED
FOR /F "eol=- tokens=1-8 delims= " %%i in ('find /I "LOG ROTATE COMPLETE" %LogPath%\2019-%MM-DD%*.log') do @echo %%i %%j %%k %%l %%m %%n %%o %%p
goto end
:WARNING
REM This line needs to be on CYAN
color 0B
FOR /F "eol=- tokens=1-8 delims= " %%i in ('find /I "LOG ROTATE COMPLETE" %LogPath%\2019-%MM-DD%*.log') do @echo %%i %%j %%k %%l %%m %%n %%o %%p
:end
pause

The code doesn't work if it find more than one result where result could be SUCCESS, WARNING, ERROR.

    **LOG BEGUN     2019-08-24 03:42:28,662
loading c:Mworksconfiglog4j2.xml
INFO  2019-08-24 03:42:34,100 Initializing configs... :: oracle.retail.mworks.config.mworksProperties [mworks]
INFO  2019-08-24 03:42:34,100 Loading Properties - jar:file:/C:/Mworks/lib/menv-engine.jar!/dtv/res/config/actions.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,115 Loading Properties - file:/C:/Mworks/cust_config/version1/actions.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,131 Loading Properties - jar:file:/C:/Mworks/lib/menv-engine.jar!/dtv/res/config/environment.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,131 Loading Properties - file:/C:/Mworks/cust_config/version1/environment.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,146 Loading Properties - jar:file:/C:/Mworks/lib/menv-engine.jar!/dtv/res/config/update.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,162 Loading Properties - file:/C:/Mworks/cust_config/version1/update.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,162 Loading Properties - jar:file:/C:/Mworks/lib/menv-engine.jar!/dtv/res/config/local.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,162 Loading Properties - file:/C:/Mworks/cust_config/version1/local.properties :: dtv.util.ResourceUtils [mworks]
INFO  2019-08-24 03:42:34,584 Loading registration data from c:\Mworks\res\data\registrationdata.json :: oracle.retail.mworks.registration.RegistrationDataManager [mworks]
INFO  2019-08-24 03:42:35,287 Gathering local Client data. :: oracle.retail.mworks.registration.RegistrationDataManager [mworks]
INFO  2019-08-24 03:42:36,334 loading jar:file:/C:/Mworks/lib/menv-engine.jar!/dtv/res/config/MBeanInfoConfig.xml :: dtv.util.config.ConfigHelper [mworks]
INFO  2019-08-24 03:42:36,883 
INFO  2019-08-24 03:42:36,883 Waiting for services to start... :: oracle.retail.mworks.mworks [mworks]
ntly running actions: [startup-lead, create-update-directories, LOG ROTATE] :: oracle.retail.mworks.action.Action [ActionExec-1]
INFO  2019-08-24 03:42:40,447 Action [CreateUpdateDirectories :: oracle.retail.mworks.atoms.CreateUpdateDirectories] complete. State: SUCCESS, Result: -----------------------------------
The text below should be in RED
----------------------------------
INFO  2019-08-24 03:42:40:03,060 LOG ROTATE complete.  Status: ERROR  Created update directories. :: oracle.retail.mworks.atoms.Atom [ActionExec-1]


INFO  2019-08-24 03:42:40,447 Currently running actions: [startup-lead, LOG ROTATE] :: oracle.retail.mworks.action.Action [ActionExec-1]
INFO  2019-08-24 03:42:40,447 Action [create-update-directories] returned state [SUCCESS] with message [Created update directories.] :: 

The text below should be in cyan
---------------------------------------
INFO  2019-08-24 04:44:03,060 LOG ROTATE complete.  Status: WARNING 

LOT OF lines  DELETED

The text below should be in green
----------------------------------------
INFO  2019-08-24 05:44:03,060 LOG ROTATE complete.  Status: SUCCESS :: oracle.retail.xenvironment.action.Action [ActionExec-2]
sample log

This is my mono color output

something like this:

@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"
)
call :chooseColor 0A "This is colored Green means SUCCESS"
echo.
call :chooseColor 0B "This is colored Cyan means WARNING"
echo.
call :chooseColor 0C "This is colored Red means ERROR"
echo.
pause
goto eof
:chooseColor
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1i
:eof

output should be like this

kallu
  • 41
  • 6
  • Edit your question and include some sample log file content. You should only need one `FOR /F` loop, and this would be better, `Find /I "Log roatate complete" ^<"%LogPath%\log.2019-%MM-DD%*.log"`. You should however implement some sort of verification of your end users input before trying to use it too! – Compo Aug 31 '19 at 08:52
  • sample log is as below---- I cannot paste the log ? – kallu Aug 31 '19 at 12:41
  • here is the log https://ufile.io/xw805ie3 – kallu Aug 31 '19 at 12:46
  • 1
    Use the [edit](https://stackoverflow.com/posts/57736435/edit) button and add it to your question, _(formatted as code using the `{}` button)_. The comment area is not for information which forms part of your question, and as StackOverflow has no control over third party sites, adding external links is not recommended. – Compo Aug 31 '19 at 12:47

4 Answers4

2

A pure batch solution using ANSI Escape codes requires a more recent Win10 version.
Difficulty is the ESC symbol hex 0x1b, dec 27 which not all editors provide easily.

Here solved using certutil to convert hex to binary.

EDIT stacked a for to the for /f with a lowercase %%f

:: Q:\Test\2019\08\31\SO_57736435.cmd
@echo off
:restart
set "MM-DD="
set /p MM-DD="Enter Month and Day (MM-DD) to Search logs for success close eg. 08-24: "
if not defined MM-DD Exit /B 0

set "LogPath=C:\Mworks\logs"
set "File=%LogPath%\log.2019-%MM-DD%*.log"
if not exist "%File%" (Echo %File% doesn't exist&pause&goto :restart)

:: ANSI Escape codes, supported with Win10 again
Call :GetCSI

FOR %%f in ("%File%") Do FOR /F "usebackq tokens=1-8* delims= " %%A in ("%%f"
) do if /I "%%A%%D%%E%%F%%G"=="INFOLogRotateComplete.Status:" (
  Call :%%H "%%A %%B %%C %%D %%E %%F %%G %%H %%I"
) Else (
  Echo:%%A %%B %%C %%D %%E %%F %%G %%H %%I
)
Pause
exit /B 0

:Warning
Echo(%CSI%36m%~1%CSI%0m
Goto :Eof

:Success
Echo(%CSI%32m%~1%CSI%0m
Goto :Eof

:Error
Echo(%CSI%31m%~1%CSI%0m
Goto :Eof

:GetCSI
echo 1B 5B>CSI.hex
Del CSI.bin >NUL 2>&1
certutil -decodehex CSI.hex CSI.bin >NUL 2>&1
Set /P CSI=<CSI.bin
Goto :Eof

enter image description here

Community
  • 1
  • 1
  • **That is neat.** _I am not able to vote._ ` Thanks for the feedback! Votes cast by those with less than 15 reputation are recorded, but do not change the publicly displayed post score. ` – kallu Aug 31 '19 at 22:43
  • Just found that the code can't handle * or () it just says file doesn't exist. The log format will be **bold**log.Year.DD-MM(1).log or (2).log **bold** and so on. So when I specify the string [log.Year.DD-MM^*.log] or [log.Year.DD-MM^(*^1)] It just says the file doesn't exist – kallu Sep 01 '19 at 06:19
  • fought hard to fix the code but I guess it's beyond my capability `set "LogPath=C:\Mworks\logs\" set "File=log.2019-%MM-DD%*.log" set combo=%Logpath%%File% FOR /F "usebackq tokens=1-8 delims= " %%A in ("%combo%" ) do if /I "%%A%%D%%E%%F%%G"=="INFOLogRotateComplete.Status:" ( Call :%%H "%%A %%B %%C %%D %%E %%F %%G %%H %%I" ) Else ( Echo:%%A %%B %%C %%D %%E %%F %%G %%H %%I ) ` if a single file name is specified it works but for wild card *.log it says `The system cannot find the file with` **.** at the last, what cmd for more than one file to pass as variable ? – kallu Sep 01 '19 at 12:31
  • The `for /f` variant I used can only parse a single file. I see two ways, either stack the `for /f` to a normal for iterating the files first - or - use the `for /f` variant parsing the output of type with with a wildcard. Both will provide a long stream with colored lines, but while the output ***can*** be redirecte to a file, it is not guaranteed you can still view the color info when outputting the redirected file. BTW `for /f` will drop empty lines. –  Sep 02 '19 at 08:45
  • required variable has been set and i passed the files in dir with this code for %%x in ("%Fpath%%Fname%%YEAR%%MM-DD%*.log") do call:process "%%~x" which than calls.. :process Call :GetCSI FOR /F "usebackq tokens=1-8 delims= " %%A in ("%~1" ) do if /I "%%A%%D%%E%%F%%G"=="INFOLogRotateComplete.Status:" ( Call :%%H "%%A %%B %%C %%D %%E %%F %%G %%H" etc etc It works but the annoyance is at the end of the output it says "The system cannot find the file ." I can't understand what it's trying to get in the end of the loop – kallu Sep 02 '19 at 09:18
  • Due to missing line breaks it makes no sense to post complete batches into comments. I'll add that `for` to above batch. –  Sep 02 '19 at 10:25
  • Hint for a new contributor: If an answer solves your question or you find it helpful you should consider to [accept the answer](http://stackoverflow.com/help/accepted-answer) and/or [vote up](https://stackoverflow.com/help/why-vote) (once you've got a [reputation](https://stackoverflow.com/help/privileges) of 15 it will be seen) –  Sep 05 '19 at 09:28
1

Batch is definitely the wrong script language for this, you need:

  • full featured Regular Expression support
  • the capability to easily color single lines

BTW your batch has a bunch of errors/shortcummings

This PowerShell script (using a single fixed file name) should do what you are after:

## create a hash table with the color mappings
$FG = @{SUCCESS='green';WARNING='cyan';ERROR='red'}

# Regular Expression with a capture group to grep 
# the Status, see https://regex101.com/r/d0swXC/1
$RE = '^INFO  (.*?) LOG ROTATE complete\. +Status: (?<Status>[^ ]+).*'

ForEach($Line in Get-Content .\file.log){
    if ($Line -match $RE){
        Write-Host -ForegroundColor $FG[$Matches.Status] $Line
    } Else {
        Write-Host $Line
    }
}

enter image description here

  • That is a great example with powershell, Running of .ps1 has been disabled. I've corrected the batch script. A log file with name log.2019-08-24(1).log should exist on C:\Mworks\logs folder – kallu Aug 31 '19 at 15:01
  • A pure batch solution using ANSI Escape codes requires a more recent Win10 version. Difficulty is the ESC symbol hex 0x1b, dec 27 which not all editors provide easily. Foregroundcolor red=`ESC[31m`, green=`ESC[32m`, cyan=`ESC[36m`, reset color=`ESC[0m` –  Aug 31 '19 at 16:24
  • I agree @LotPings, It's very tough with limited feature of DOS commands using only pure batch scripts – kallu Aug 31 '19 at 21:12
1

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
Zilog80
  • 2,534
  • 2
  • 15
  • 20
0

For windows 10, In native batch replacing %~dp0input.txt with the logs path:

::: 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 200,150
  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%
  If not "!Switch[S]!" == ""    (Set "_String_=!Switch[S]:{FS}=/!")Else Set "_String_="%\n%
  ^< nul set /P "=!_Buffer_!!_Cursor_!!_Ypos_!!_YOffset_!!_Xpos_!!_XOffset_!!_Delete_!!_Insert_!!_Color_!!_LineClear_!!_String_!%\E%[0m"%\n%
  If "!Switch[N]!" == "true"    Echo(%\n%
 ) Else Set args=

:# enable macro using Setlocal EnableExtensions EnableDelayedExpansion

 CLS
 If "%~1." == "/?." (
  Setlocal EnableExtensions EnableDelayedExpansion
  %$Cout% /help
  Goto :EOF
 )

 For /F "usebackq Delims=" %%G in ("%~dp0input.txt")Do (
  Set "Line=%%G"
  Setlocal EnableExtensions EnableDelayedExpansion
  If not "!Line:SUCCESS=!" == "!Line!" (
   %$cout% /C 32 /N /S "!line:/={FS}!"
  ) Else If not "!Line:WARNING=!" == "!Line!" (
   %$cout% /C 36 /N /S "!line:/={FS}!"
  ) Else If not "!Line:ERROR=!" == "!Line!" (
   %$cout% /C 31 /N /S "!line:/={FS}!"
  ) Else (
   %$cout% /N /S "!line:/={FS}!"
  )
  Endlocal
 )
 
Goto :eof

T3RR0R
  • 2,747
  • 3
  • 10
  • 25