How to have multiple colors in a Windows batch file?

前端 未结 12 1474
我寻月下人不归
我寻月下人不归 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: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
    

提交回复
热议问题