How to have multiple colors in a Windows batch file?

前端 未结 12 1408
我寻月下人不归
我寻月下人不归 2020-11-22 01:05

I was wondering if its possible to have different colored text on the same line in a Windows batch file, for example if it says

echo hi world
相关标签:
12条回答
  • 2020-11-22 01:32

    Yes, it is possible with cmdcolor:

    echo \033[32mhi \033[92mworld
    

    hi will be dark green, and world - light green.

    0 讨论(0)
  • 2020-11-22 01:36

    Actually this can be done without creating a temporary file. The method described by jeb and dbenham will work even with a target file that contains no backspaces. The critical point is that the line recognized by findstr.exe must not end with a CRLF. So the obvious text file to scan with a line not ending with a CRLF is the invoking batch itself, provided that we end it with such a line! Here's an updated example script working this way...

    Changes from the previous example:

    • Uses a single dash on the last line as the searchable string. (Must be short and not appear anywhere else like this in the batch.)
    • Renamed routines and variables to be a little more object-oriented :-)
    • Removed one call level, to slightly improve performance.
    • Added comments (Beginning with :# to look more like most other scripting languages.)

    @echo off
    setlocal
    
    call :Echo.Color.Init
    
    goto main
    
    :Echo.Color %1=Color %2=Str [%3=/n]
    setlocal enableDelayedExpansion
    set "str=%~2"
    :Echo.Color.2
    :# Replace path separators in the string, so that the final path still refers to the current path.
    set "str=a%ECHO.DEL%!str:\=a%ECHO.DEL%\..\%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%!"
    set "str=!str:/=a%ECHO.DEL%/..\%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%!"
    set "str=!str:"=\"!"
    :# Go to the script directory and search for the trailing -
    pushd "%ECHO.DIR%"
    findstr /p /r /a:%~1 "^^-" "!str!\..\!ECHO.FILE!" nul
    popd
    :# Remove the name of this script from the output. (Dependant on its length.)
    for /l %%n in (1,1,12) do if not "!ECHO.FILE:~%%n!"=="" <nul set /p "=%ECHO.DEL%"
    :# Remove the other unwanted characters "\..\: -"
    <nul set /p "=%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%%ECHO.DEL%"
    :# Append the optional CRLF
    if not "%~3"=="" echo.
    endlocal & goto :eof
    
    :Echo.Color.Var %1=Color %2=StrVar [%3=/n]
    if not defined %~2 goto :eof
    setlocal enableDelayedExpansion
    set "str=!%~2!"
    goto :Echo.Color.2
    
    :Echo.Color.Init
    set "ECHO.COLOR=call :Echo.Color"
    set "ECHO.DIR=%~dp0"
    set "ECHO.FILE=%~nx0"
    set "ECHO.FULL=%ECHO.DIR%%ECHO.FILE%"
    :# Use prompt to store a backspace into a variable. (Actually backspace+space+backspace)
    for /F "tokens=1 delims=#" %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do set "ECHO.DEL=%%a"
    goto :eof
    
    :main
    call :Echo.Color 0a "a"
    call :Echo.Color 0b "b"
    set "txt=^" & call :Echo.Color.Var 0c txt
    call :Echo.Color 0d "<"
    call :Echo.Color 0e ">"
    call :Echo.Color 0f "&"
    call :Echo.Color 1a "|"
    call :Echo.Color 1b " "
    call :Echo.Color 1c "%%%%"
    call :Echo.Color 1d ^"""
    call :Echo.Color 1e "*"
    call :Echo.Color 1f "?"
    :# call :Echo.Color 2a "!"
    call :Echo.Color 2b "."
    call :Echo.Color 2c ".."
    call :Echo.Color 2d "/"
    call :Echo.Color 2e "\"
    call :Echo.Color 2f "q:" /n
    echo(
    set complex="c:\hello world!/.\..\\a//^<%%>&|!" /^^^<%%^>^&^|!\
    call :Echo.Color.Var 74 complex /n
    
    exit /b
    
    :# The following line must be last and not end by a CRLF.
    -
    

    PS. I'm having a problem with the output of the ! character that you did not have in the previous example. (Or at least you did not have the same symptoms.) To be investigated.

    0 讨论(0)
  • 2020-11-22 01:36

    jeb's edited answer comes close to solving all the issues. But it has problems with the following strings:

    "a\b\"
    "a/b/"
    "\"
    "/"
    "."
    ".."
    "c:"
    

    I've modified his technique to something that I think can truly handle any string of printable characters, except for length limitations.

    Other improvements:

    • Uses the %TEMP% location for the temp file, so no longer need write access to the current directory.

    • Created 2 variants, one takes a string literal, the other the name of a variable containing the string. The variable version is generally less convenient, but it eliminates some special character escape issues.

    • Added the /n option as an optional 3rd parameter to append a newline at the end of the output.

    Backspace does not work across a line break, so the technique can have problems if the line wraps. For example, printing a string with length between 74 - 79 will not work properly if the console has a line width of 80.

    @echo off
    setlocal
    
    call :initColorPrint
    
    call :colorPrint 0a "a"
    call :colorPrint 0b "b"
    set "txt=^" & call :colorPrintVar 0c txt
    call :colorPrint 0d "<"
    call :colorPrint 0e ">"
    call :colorPrint 0f "&"
    call :colorPrint 1a "|"
    call :colorPrint 1b " "
    call :colorPrint 1c "%%%%"
    call :colorPrint 1d ^"""
    call :colorPrint 1e "*"
    call :colorPrint 1f "?"
    call :colorPrint 2a "!"
    call :colorPrint 2b "."
    call :colorPrint 2c ".."
    call :colorPrint 2d "/"
    call :colorPrint 2e "\"
    call :colorPrint 2f "q:" /n
    echo(
    set complex="c:\hello world!/.\..\\a//^<%%>&|!" /^^^<%%^>^&^|!\
    call :colorPrintVar 74 complex /n
    
    call :cleanupColorPrint
    
    exit /b
    
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    :colorPrint Color  Str  [/n]
    setlocal
    set "str=%~2"
    call :colorPrintVar %1 str %3
    exit /b
    
    :colorPrintVar  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
    
    :initColorPrint
    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
    
    :cleanupColorPrint
    del "%temp%\x"
    exit /b
    


    UPDATE 2012-11-27

    This method fails on XP because FINDSTR displays backspace as a period on the screen. jeb's original answer works on XP, albeit with the limitations already noted


    UPDATE 2012-12-14

    There has been a lot of development activity at DosTips and SS64. It turns out that FINDSTR also corrupts file names containing extended ASCII if supplied on the command line. I've updated my FINDSTR Q&A.

    Below is a version that works on XP and supports ALL single byte characters except 0x00 (nul), 0x0A (linefeed), and 0x0D (carriage return). However, when running on XP, most control characters will display as dots. This is an inherent feature of FINDSTR on XP that cannot be avoided.

    Unfortunately, adding support for XP and for extended ASCII characters slows the routine down :-(

    Just for fun, I grabbed some color ASCII art from joan stark's ASCII Art Gallery and adapted it for use with ColorPrint. I added a :c entry point just for shorthand, and to handle an issue with quote literals.

    @echo off
    setlocal disableDelayedExpansion
    set q=^"
    echo(
    echo(
    call :c 0E "                ,      .-;" /n
    call :c 0E "             ,  |\    / /  __," /n
    call :c 0E "             |\ '.`-.|  |.'.-'" /n
    call :c 0E "              \`'-:  `; : /" /n
    call :c 0E "               `-._'.  \'|" /n
    call :c 0E "              ,_.-=` ` `  ~,_" /n
    call :c 0E "               '--,.    "&call :c 0c ".-. "&call :c 0E ",=!q!." /n
    call :c 0E "                 /     "&call :c 0c "{ "&call :c 0A "* "&call :c 0c ")"&call :c 0E "`"&call :c 06 ";-."&call :c 0E "}" /n
    call :c 0E "                 |      "&call :c 0c "'-' "&call :c 06 "/__ |" /n
    call :c 0E "                 /          "&call :c 06 "\_,\|" /n
    call :c 0E "                 |          (" /n
    call :c 0E "             "&call :c 0c "__ "&call :c 0E "/ '          \" /n
    call :c 02 "     /\_    "&call :c 0c "/,'`"&call :c 0E "|     '   "&call :c 0c ".-~!q!~~-." /n
    call :c 02 "     |`.\_ "&call :c 0c "|   "&call :c 0E "/  ' ,    "&call :c 0c "/        \" /n
    call :c 02 "   _/  `, \"&call :c 0c "|  "&call :c 0E "; ,     . "&call :c 0c "|  ,  '  . |" /n
    call :c 02 "   \   `,  "&call :c 0c "|  "&call :c 0E "|  ,  ,   "&call :c 0c "|  :  ;  : |" /n
    call :c 02 "   _\  `,  "&call :c 0c "\  "&call :c 0E "|.     ,  "&call :c 0c "|  |  |  | |" /n
    call :c 02 "   \`  `.   "&call :c 0c "\ "&call :c 0E "|   '     "&call :c 0A "|"&call :c 0c "\_|-'|_,'\|" /n
    call :c 02 "   _\   `,   "&call :c 0A "`"&call :c 0E "\  '  . ' "&call :c 0A "| |  | |  |           "&call :c 02 "__" /n
    call :c 02 "   \     `,   "&call :c 0E "| ,  '    "&call :c 0A "|_/'-|_\_/     "&call :c 02 "__ ,-;` /" /n
    call :c 02 "    \    `,    "&call :c 0E "\ .  , ' .| | | | |   "&call :c 02 "_/' ` _=`|" /n
    call :c 02 "     `\    `,   "&call :c 0E "\     ,  | | | | |"&call :c 02 "_/'   .=!q!  /" /n
    call :c 02 "     \`     `,   "&call :c 0E "`\      \/|,| ;"&call :c 02 "/'   .=!q!    |" /n
    call :c 02 "      \      `,    "&call :c 0E "`\' ,  | ; "&call :c 02 "/'    =!q!    _/" /n
    call :c 02 "       `\     `,  "&call :c 05 ".-!q!!q!-. "&call :c 0E "': "&call :c 02 "/'    =!q!     /" /n
    call :c 02 "    jgs _`\    ;"&call :c 05 "_{  '   ; "&call :c 02 "/'    =!q!      /" /n
    call :c 02 "       _\`-/__"&call :c 05 ".~  `."&call :c 07 "8"&call :c 05 ".'.!q!`~-. "&call :c 02 "=!q!     _,/" /n
    call :c 02 "    __\      "&call :c 05 "{   '-."&call :c 07 "|"&call :c 05 ".'.--~'`}"&call :c 02 "    _/" /n
    call :c 02 "    \    .=!q!` "&call :c 05 "}.-~!q!'"&call :c 0D "u"&call :c 05 "'-. '-..'  "&call :c 02 "__/" /n
    call :c 02 "   _/  .!q!    "&call :c 05 "{  -'.~('-._,.'"&call :c 02 "\_,/" /n
    call :c 02 "  /  .!q!    _/'"&call :c 05 "`--; ;  `.  ;" /n
    call :c 02 "   .=!q!  _/'      "&call :c 05 "`-..__,-'" /n
    call :c 02 "    __/'" /n
    echo(
    
    exit /b
    
    :c
    setlocal enableDelayedExpansion
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    :colorPrint Color  Str  [/n]
    setlocal
    set "s=%~2"
    call :colorPrintVar %1 s %3
    exit /b
    
    :colorPrintVar  Color  StrVar  [/n]
    if not defined DEL call :initColorPrint
    setlocal enableDelayedExpansion
    pushd .
    ':
    cd \
    set "s=!%~2!"
    :: The single blank line within the following IN() clause is critical - DO NOT REMOVE
    for %%n in (^"^
    
    ^") do (
      set "s=!s:\=%%~n\%%~n!"
      set "s=!s:/=%%~n/%%~n!"
      set "s=!s::=%%~n:%%~n!"
    )
    for /f delims^=^ eol^= %%s in ("!s!") do (
      if "!" equ "" setlocal disableDelayedExpansion
      if %%s==\ (
        findstr /a:%~1 "." "\'" nul
        <nul set /p "=%DEL%%DEL%%DEL%"
      ) else if %%s==/ (
        findstr /a:%~1 "." "/.\'" nul
        <nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%"
      ) else (
        >colorPrint.txt (echo %%s\..\')
        findstr /a:%~1 /f:colorPrint.txt "."
        <nul set /p "=%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%%DEL%"
      )
    )
    if /i "%~3"=="/n" echo(
    popd
    exit /b
    
    
    :initColorPrint
    for /f %%A in ('"prompt $H&for %%B in (1) do rem"') do set "DEL=%%A %%A"
    <nul >"%temp%\'" set /p "=."
    subst ': "%temp%" >nul
    exit /b
    
    
    :cleanupColorPrint
    2>nul del "%temp%\'"
    2>nul del "%temp%\colorPrint.txt"
    >nul subst ': /d
    exit /b
    
    0 讨论(0)
  • 2020-11-22 01:36

    After my previous answer was deleted for failing to include the code, on the basis the Ansi characters used can not be displayed by stack overflow rendering the presence of the code somewhat pointless given it was based on them, I have redesigned the code to include the method of populating the Escape character detailed by @Sam Hasler

    In the process, I've also taken out all the subroutines in favor of macro's, and adapted my approach to passing parameters to the macro's.

    All macro's balance the Setlocal / Endlocal pairings to prevent exceeding recursion level's through use of ^&^& endlocal after completeing their handling of the Args.

    The std.out macro also demonstrates how to adapt the macros to store output into variables that survive past the Endlocal barrier.

    @Echo off & Mode 1000
    
    ::: / Creates two variables with one character DEL=Ascii-08 and /AE=Ascii-27 escape code. only /AE is used
    ::: - http://www.dostips.com/forum/viewtopic.php?t=1733
    ::: - https://stackoverflow.com/a/34923514/12343998
    :::
    ::: - DEL and ESC can be used  with and without DelayedExpansion, except during Macro Definition
        Setlocal
        For /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
            Endlocal
            Set "DEL=%%a"
            Set "/AE=%%b"
        )
    ::: \
    
    ::: / Establish Environment for macro Definition
        Setlocal DisableDelayedExpansion
    
        (Set LF=^
    
    
        %= NewLine =%)
    
        Set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
    ::: \
    
    ::: / Ascii code variable assignment
    ::: - Variables used for cursor Positiong Ascii codes in the form of bookends to prevent ansi escape code disrupting macro definition
        Set "[=%/AE%["
        Set "]=H"
    ::: - define Variables for Ascii color code values
        Set "Red=%/AE%[31m"
        Set "Green=%/AE%[32m"
        Set "Yellow=%/AE%[33m"
        Set "Blue=%/AE%[34m"
        Set "Purple=%/AE%[35m"
        Set "Cyan=%/AE%[36m"
        Set "White=%/AE%[37m"
        Set "Grey=%/AE%[90m"
        Set "Pink=%/AE%[91m"
        Set "BrightGreen=%/AE%[92m"
        Set "Beige=%/AE%[93m"
        Set "Aqua=%/AE%[94m"
        Set "Magenta=%/AE%[95m"
        Set "Teal=%/AE%[96m"
        Set "BrightWhite=%/AE%[97m"
        Set "Off=%/AE%[0m"
    ::: \
    
    ::: / mini-Macro to Pseudo pipe complex strings into Macros.
        Set "Param|=Set Arg-Output="
    ::: \
    
    ::: / Macro for outputing to cursor position Arg1 in color Arg2
        Set Pos.Color=^&for /L %%n in (1 1 2) do if %%n==2 (%\n%
            For /F "tokens=1,2 delims=, " %%G in ("!argv!") do (%\n%
                Echo(![!%%G!]!!%%H!!Arg-Output!!Off!^&^&Endlocal%\n%
            ) %\n%
        ) ELSE setlocal enableDelayedExpansion ^& set argv=, 
    ::: \
    
    ::: / Macro variable for creating a Colored prompt with pause at Cursor pos Arg1 in Color Arg2
        Set Prompt.Pause=^&for /L %%n in (1 1 2) do if %%n==2 (%\n%
            For /F "tokens=1,2 delims=, " %%G in ("!argv!") do (%\n%
            Echo.!/AE![%%G!]!!/AE![%%Hm!Arg-Output!!Off!%\n%
            pause^>nul ^&^& Endlocal%\n%
            ) %\n%
        ) ELSE setlocal enableDelayedExpansion ^& set argv=, 
    ::: \
    
    ::: / Macro variable for outputing to stdout on a new line with selected color Arg1 and store output to VarName Arg2
        Set std.out=^&for /L %%n in (1 1 2) do if %%n==2 (%\n%
            For /F "tokens=1,2 delims=, " %%G in ("!argv!") do (%\n%
            Echo.!/AE![%%Gm!Arg-Output!!Off!^&^& Endlocal ^&(Set %%H=!Arg-Output!)%\n%
            ) %\n%
        ) ELSE setlocal enableDelayedExpansion ^& set argv=, 
    ::: \
    
    ::: / Stringlength Macro. Not utilized in this example.
    ::: Usage: %Param|%string or expanded variable%get.strLen% ResultVar
    Set get.strLen=^&for /L %%n in (1 1 2) do if %%n==2 (%\n%
        For /F "tokens=1,* delims=, " %%G in ("!argv!") do (%\n%
            Set tmpLen=!Arg-Output!%\n%
            Set LenTrim=Start%\n%
            For /L %%a in (1,1,250) Do (%\n%
                IF NOT "!LenTrim!"=="" (%\n%
                    Set LenTrim=!tmpLen:~0,-%%a!%\n%
                    If "!LenTrim!"=="" Echo.>nul ^&^& Endlocal ^&(Set %%G=%%a)%\n%
                )%\n%
            ) %\n%
        ) %\n%
    ) ELSE setlocal enableDelayedExpansion ^& set argv=, 
    ::: \
    
    
    ::: / Create Script break for Subroutines. Subroutines not utilized in this example
        Goto :main
    ::: \
    
    ::: / Subroutines
    
    ::: \
    
    ::: / Script main Body
    :::::: - Example usage
    :main
    
        Setlocal EnableDelayedExpansion
    
    For %%A in ("31,1,37" "41,1,47" "90,1,97" "100,1,107") do For /L %%B in (%%~A) Do %Param|%[Color Code = %%B.]%std.out% %%B AssignVar
    
            Set "XP=20"
        For %%A in (red aqua white brightwhite brightgreen beige blue magenta green pink cyan grey yellow purple teal) do (
            Set /A XP+=1
            Set /A YP+=1
            Set "output=%%A !YP!;!XP!"
            %Param|%Cursor Pos: !YP!;!XP!, %%A %Pos.Color% !YP!;!XP! %%A
        )
        %Param|%Example %green%Complete.%prompt.pause% 32;10 31
    
        Endlocal
    
    Exit /B
    ::: \ End Script
    

    My thanks to @Martijn Pieters for deleting my previous answer. In rewriting my code I also discovered for myself some new ways to manipulate macro's.

    Script output

    0 讨论(0)
  • 2020-11-22 01:37

    Combining dbenham's bird and syntax with skrebbel's powershell write-host method, it seems that powershell can render complex art more quickly than dbenham's pure batch method (well, after powershell has been primed once, anyway). Minimal massaging of the strings are needed, although I haven't tested this with anything other than the bird. If you want a bright green end-of-transmission character for example, you may be out of luck. :)

    This method requires echoing out to a temp file, simply because invoking powershell for each call :c takes forever, and it's much faster to queue the output for one powershell invocation. But it does have the advantage of simplicity and efficiency.

    @echo off
    setlocal disableDelayedExpansion
    set q=^"
    echo(
    echo(
    call :c 0E "                ,      .-;" /n
    call :c 0E "             ,  |\    / /  __," /n
    call :c 0E "             |\ '.`-.|  |.'.-'" /n
    call :c 0E "              \`'-:  `; : /" /n
    call :c 0E "               `-._'.  \'|" /n
    call :c 0E "              ,_.-=` ` `  ~,_" /n
    call :c 0E "               '--,.    "&call :c 0c ".-. "&call :c 0E ",=!q!." /n
    call :c 0E "                 /     "&call :c 0c "{ "&call :c 0A "* "&call :c 0c ")"&call :c 0E "`"&call :c 06 ";-."&call :c 0E "}" /n
    call :c 0E "                 |      "&call :c 0c "'-' "&call :c 06 "/__ |" /n
    call :c 0E "                 /          "&call :c 06 "\_,\|" /n
    call :c 0E "                 |          (" /n
    call :c 0E "             "&call :c 0c "__ "&call :c 0E "/ '          \" /n
    call :c 02 "     /\_    "&call :c 0c "/,'`"&call :c 0E "|     '   "&call :c 0c ".-~!q!~~-." /n
    call :c 02 "     |`.\_ "&call :c 0c "|   "&call :c 0E "/  ' ,    "&call :c 0c "/        \" /n
    call :c 02 "   _/  `, \"&call :c 0c "|  "&call :c 0E "; ,     . "&call :c 0c "|  ,  '  . |" /n
    call :c 02 "   \   `,  "&call :c 0c "|  "&call :c 0E "|  ,  ,   "&call :c 0c "|  :  ;  : |" /n
    call :c 02 "   _\  `,  "&call :c 0c "\  "&call :c 0E "|.     ,  "&call :c 0c "|  |  |  | |" /n
    call :c 02 "   \`  `.   "&call :c 0c "\ "&call :c 0E "|   '     "&call :c 0A "|"&call :c 0c "\_|-'|_,'\|" /n
    call :c 02 "   _\   `,   "&call :c 0A "`"&call :c 0E "\  '  . ' "&call :c 0A "| |  | |  |           "&call :c 02 "__" /n
    call :c 02 "   \     `,   "&call :c 0E "| ,  '    "&call :c 0A "|_/'-|_\_/     "&call :c 02 "__ ,-;` /" /n
    call :c 02 "    \    `,    "&call :c 0E "\ .  , ' .| | | | |   "&call :c 02 "_/' ` _=`|" /n
    call :c 02 "     `\    `,   "&call :c 0E "\     ,  | | | | |"&call :c 02 "_/'   .=!q!  /" /n
    call :c 02 "     \`     `,   "&call :c 0E "`\      \/|,| ;"&call :c 02 "/'   .=!q!    |" /n
    call :c 02 "      \      `,    "&call :c 0E "`\' ,  | ; "&call :c 02 "/'    =!q!    _/" /n
    call :c 02 "       `\     `,  "&call :c 05 ".-!q!!q!-. "&call :c 0E "': "&call :c 02 "/'    =!q!     /" /n
    call :c 02 "    jgs _`\    ;"&call :c 05 "_{  '   ; "&call :c 02 "/'    =!q!      /" /n
    call :c 02 "       _\`-/__"&call :c 05 ".~  `."&call :c 07 "8"&call :c 05 ".'.!q!`~-. "&call :c 02 "=!q!     _,/" /n
    call :c 02 "    __\      "&call :c 05 "{   '-."&call :c 07 "|"&call :c 05 ".'.--~'`}"&call :c 02 "    _/" /n
    call :c 02 "    \    .=!q!` "&call :c 05 "}.-~!q!'"&call :c 0D "u"&call :c 05 "'-. '-..'  "&call :c 02 "__/" /n
    call :c 02 "   _/  .!q!    "&call :c 05 "{  -'.~('-._,.'"&call :c 02 "\_,/" /n
    call :c 02 "  /  .!q!    _/'"&call :c 05 "`--; ;  `.  ;" /n
    call :c 02 "   .=!q!  _/'      "&call :c 05 "`-..__,-'" /n
    call :c 02 "    __/'" /n
    
    if exist "%temp%\color.psm1" (
        powershell -command "&{set-executionpolicy remotesigned; Import-Module '%temp%\color.psm1'}"
        del "%temp%\color.psm1"
    )
    
    echo(
    
    exit /b
    
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    :c <color pair> <string> </n>
    setlocal enabledelayedexpansion
    set "colors=0-black;1-darkblue;2-darkgreen;3-darkcyan;4-darkred;5-darkmagenta;6-darkyellow;7-gray;8-darkgray;9-blue;a-green;b-cyan;c-red;d-magenta;e-yellow;f-white"
    set "p=%~1"
    set "bg=!colors:*%p:~0,1%-=!"
    set bg=%bg:;=&rem.%
    set "fg=!colors:*%p:~-1%-=!"
    set fg=%fg:;=&rem.%
    
    if not "%~3"=="/n" set "br=-nonewline"
    set "str=%~2" & set "str=!str:'=''!"
    
    >>"%temp%\color.psm1" echo write-host '!str!' -foregroundcolor '%fg%' -backgroundcolor '%bg%' %br%
    endlocal
    

    Result:

    enter image description here

    0 讨论(0)
  • 2020-11-22 01:37

    If your console supports ANSI colour codes (e.g. ConEmu, Clink or ANSICON) you can do this:

    SET    GRAY=%ESC%[0m
    SET     RED=%ESC%[1;31m
    SET   GREEN=%ESC%[1;32m
    SET  ORANGE=%ESC%[0;33m
    SET    BLUE=%ESC%[0;34m
    SET MAGENTA=%ESC%[0;35m
    SET    CYAN=%ESC%[1;36m
    SET   WHITE=%ESC%[1;37m
    

    where ESC variable contains ASCII character 27.

    I found a way to populate the ESC variable here: http://www.dostips.com/forum/viewtopic.php?p=6827#p6827 and using tasklist it's possible to test what DLLs are loaded into a process.

    The following script gets the process ID of the cmd.exe that the script is running in. Checks if it has a dll that will add ANSI support injected, and then sets colour variables to contain escape sequences or be empty depending on whether colour is supported or not.

    @echo off
    
    call :INIT_COLORS
    
    echo %RED%RED %GREEN%GREEN %ORANGE%ORANGE %BLUE%BLUE %MAGENTA%MAGENTA %CYAN%CYAN %WHITE%WHITE %GRAY%GRAY
    
    :: pause if double clicked on instead of run from command line.
    SET interactive=0
    ECHO %CMDCMDLINE% | FINDSTR /L %COMSPEC% >NUL 2>&1
    IF %ERRORLEVEL% == 0 SET interactive=1
    @rem ECHO %CMDCMDLINE% %COMSPEC% %interactive%
    IF "%interactive%"=="1" PAUSE
    EXIT /B 0
    Goto :EOF
    
    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    : SUBROUTINES                                                          :
    ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
    ::::::::::::::::::::::::::::::::
    :INIT_COLORS
    ::::::::::::::::::::::::::::::::
    
    call :supportsANSI
    if ERRORLEVEL 1 (
      SET GREEN=
      SET RED=
      SET GRAY=
      SET WHITE=
      SET ORANGE=
      SET CYAN=
    ) ELSE (
    
      :: If you can, insert ASCII CHAR 27 after equals and remove BL.String.CreateDEL_ESC routine
      set "ESC="
      :: use this if can't type ESC CHAR, it's more verbose, but you can copy and paste it
      call :BL.String.CreateDEL_ESC
    
      SET    GRAY=%ESC%[0m
      SET     RED=%ESC%[1;31m
      SET   GREEN=%ESC%[1;32m
      SET  ORANGE=%ESC%[0;33m
      SET    BLUE=%ESC%[0;34m
      SET MAGENTA=%ESC%[0;35m
      SET    CYAN=%ESC%[1;36m
      SET   WHITE=%ESC%[1;37m
    )
    
    exit /b
    
    ::::::::::::::::::::::::::::::::
    :BL.String.CreateDEL_ESC
    ::::::::::::::::::::::::::::::::
    :: http://www.dostips.com/forum/viewtopic.php?t=1733
    ::
    :: Creates two variables with one character DEL=Ascii-08 and ESC=Ascii-27
    :: DEL and ESC can be used  with and without DelayedExpansion
    setlocal
    for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (
      ENDLOCAL
      set "DEL=%%a"
      set "ESC=%%b"
      goto :EOF
    )
    
    ::::::::::::::::::::::::::::::::
    :supportsANSI
    ::::::::::::::::::::::::::::::::
    :: returns ERRORLEVEL 0 - YES, 1 - NO
    ::
    :: - Tests for ConEmu, ANSICON and Clink
    :: - Returns 1 - NO support, when called via "CMD /D" (i.e. no autoruns / DLL injection)
    ::   on a system that would otherwise support ANSI.
    
    if "%ConEmuANSI%" == "ON" exit /b 0
    
    call :getPID PID
    
    setlocal
    
    for /f usebackq^ delims^=^"^ tokens^=^* %%a in (`tasklist /fi "PID eq %PID%" /m /fo CSV`) do set "MODULES=%%a"
    
    set MODULES=%MODULES:"=%
    set NON_ANSI_MODULES=%MODULES%
    
    :: strip out ANSI dlls from module list:
    :: ANSICON adds ANSI64.dll or ANSI32.dll
    set "NON_ANSI_MODULES=%NON_ANSI_MODULES:ANSI=%"
    :: ConEmu attaches ConEmuHk but ConEmu also sets ConEmuANSI Environment VAR
    :: so we've already checked for that above and returned early.
    @rem set "NON_ANSI_MODULES=%NON_ANSI_MODULES:ConEmuHk=%"
    :: Clink supports ANSI https://github.com/mridgers/clink/issues/54
    set "NON_ANSI_MODULES=%NON_ANSI_MODULES:clink_dll=%"
    
    if "%MODULES%" == "%NON_ANSI_MODULES%" endlocal & exit /b 1
    endlocal
    
    exit /b 0
    
    ::::::::::::::::::::::::::::::::
    :getPID  [RtnVar]
    ::::::::::::::::::::::::::::::::
    :: REQUIREMENTS:
    ::
    :: Determine the Process ID of the currently executing script,
    :: but in a way that is multiple execution safe especially when the script can be executing multiple times
    ::   - at the exact same time in the same millisecond,
    ::   - by multiple users,
    ::   - in multiple window sessions (RDP),
    ::   - by privileged and non-privileged (e.g. Administrator) accounts,
    ::   - interactively or in the background.
    ::   - work when the cmd.exe window cannot appear
    ::     e.g. running from TaskScheduler as LOCAL SERVICE or using the "Run whether user is logged on or not" setting
    ::
    :: https://social.msdn.microsoft.com/Forums/vstudio/en-US/270f0842-963d-4ed9-b27d-27957628004c/what-is-the-pid-of-the-current-cmdexe?forum=msbuild
    ::
    :: http://serverfault.com/a/654029/306
    ::
    :: Store the Process ID (PID) of the currently running script in environment variable RtnVar.
    :: If called without any argument, then simply write the PID to stdout.
    ::
    ::
    setlocal disableDelayedExpansion
    :getLock
    set "lock=%temp%\%~nx0.%time::=.%.lock"
    set "uid=%lock:\=:b%"
    set "uid=%uid:,=:c%"
    set "uid=%uid:'=:q%"
    set "uid=%uid:_=:u%"
    setlocal enableDelayedExpansion
    set "uid=!uid:%%=:p!"
    endlocal & set "uid=%uid%"
    2>nul ( 9>"%lock%" (
      for /f "skip=1" %%A in (
        'wmic process where "name='cmd.exe' and CommandLine like '%%<%uid%>%%'" get ParentProcessID'
      ) do for %%B in (%%A) do set "PID=%%B"
      (call )
    ))||goto :getLock
    del "%lock%" 2>nul
    endlocal & if "%~1" equ "" (echo(%PID%) else set "%~1=%PID%"
    exit /b
    
    0 讨论(0)
提交回复
热议问题