每行批处理颜色

26

在批处理中,每行可以有不同的颜色。

例如,如果您有一个批处理文件,内容如下:

"Hello" "How are you?"

你可以让"Hello"显示为蓝色,"How are you"显示为绿色吗?

(我知道关于color命令,并且它会同时改变背景和文本的颜色)


3
此问题的副本(标题不佳):如何制作一个多功能批处理程序? - Hand-E-Food
@Hand-E-Food 谢谢。我明白你所说的标题不太好,但是有没有一种方法可以在不设置它的情况下完成这个任务呢?我问这个问题是因为我正在制作一个批处理游戏,这将是一个很好的功能,所以我希望能够将其提供给其他人而不必更改任何计算机设置。 - Gareth Jones
1
您不需要更改计算机设置,只需将此批处理文件包含在与游戏相同的文件夹中即可。另一个问题的发布者希望这是系统范围内的,因此我已将其添加到答案中。您只需要脚本标题为CEcho.bat的脚本即可。 - Hand-E-Food
1
可能是重复的问题:如何在Windows批处理文件中使用多个颜色? - Charlie
如何使用命令行参数控制打印多颜色文本的批处理程序? - phuclv
11个回答

30

您可以不需要任何外部程序就能完成这个操作。以下是我几天前发现的,非常好用。

@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"
)
echo say the name of the colors, don't read

call :ColorText 0a "blue"
call :ColorText 0C "green"
call :ColorText 0b "red"
echo(
call :ColorText 19 "yellow"
call :ColorText 2F "black"
call :ColorText 4e "white"

goto :eof

:ColorText
echo off
<nul set /p ".=%DEL%" > "%~2"
findstr /v /a:%1 /R "^$" "%~2" nul
del "%~2" > nul 2>&1
goto :eof
感谢jeb
您可以在他的帖子中找到此内容

3
唯一的问题似乎是,您无法使用某些字符,例如 :*?" - jeb
2
现在您也可以使用特殊字符,如 :/*?<>"如何在批处理文件中使用多种颜色? - jeb
1
找不到 findstr - Jorjon

5
以下批处理文件创建名为 COLORMSG.COM 的文件(重命名为您想要的名称),以以下方式显示带颜色的消息:colormsg color "Message"
@echo off
(
echo e100
echo 0F B6 0E 80 00 E3 4F BF 81 00 B0 20 FC F3 AE 74
echo e110
echo 45 E3 43 8A 45 FF E8 43 00 80 3D 20 74 0E C0 E0
echo e120
echo 04 8A E0 8A 05 E8 34 00 0A C4 47 49 E3 28 32 E4
echo e130
echo 50 B0 22 F2 AE 75 1F E3 1D 8B F7 8B D1 F2 AE 75
echo e140
echo 01 41 2B D1 74 10 8B CA 5B B0 20 B4 09 CD 10 AC
echo e150
echo B4 0E CD 10 E2 F9 32 C0 B4 4C CD 21 3C 61 72 02
echo e160
echo 2C 20 3C 41 72 02 2C 07 2C 30 C3
echo rcx
echo 6b
echo w
echo q
) | debug colormsg.com > nul

在消息后,光标不会移动到新行,因此可能会在同一行显示多个消息。为了更轻松地设置颜色,您可以使用以下定义:

set Black=0&       set Gray=8
set Blue=1&        set LightBlue=9
set Green=2&       set LightGreen=A
set Aqua=3&        set LightAqua=B
set Red=4&         set LightRed=C
set Purple=5&      set LightPurple=D
set Yellow=6&      set LightYellow=E
set White=7&       set BrightWhite=F

例如:

colormsg %Yellow% "Message in yellow  "
colormsg %Blue%%BrightWhite% "Bright white text over blue background"

这是一个更大的例子:

@echo off
setlocal EnableDelayedExpansion
set i=0
for %%c in (Blk Blu Grn Aqu Red Pur Yel Whi) do (
    set color[!i!]=%%c
    set /A i+=1
)
for /L %%j in (0,1,7) do (
    set color[!i!]=L!color[%%j]:~0,-1!
    set /A i+=1
)    
cls
echo/
echo           BACKGROUND / FOREGROUND COMBINATIONS OF AVAILABLE SCREEN COLORS
echo/
echo/
colormsg 7 "ForeGrnd:"
for /L %%i in (0,1,15) do colormsg 7 " !color[%%i]!"
echo/
echo BackGrnd
set i=0
for /L %%b in (0,1,9) do call :ShowLine %%b
for %%b in (A B C D E F) do call :ShowLine %%b
echo/
goto :eof

:ShowLine
colormsg 7 "   !color[%i%]!   "
set /A i+=1
for /L %%f in (0,1,9) do colormsg %1%%f " %1%%f "
for %%f in (A B C D E F) do colormsg %1%%f " %1%%f "
echo/

嘿,Gareth,这是给你的奖励!以下批处理文件创建TEXTPOS.COM文件(可根据您的需要重命名),以此方式移动光标到任何行和列:textpos line col

@echo off
(
echo e100
echo 0F B6 0E 80 00 E3 2D BF 81 00 B0 20 FC F3 AE 74
echo e110
echo 23 E3 21 8D 75 FF E8 45 00 3C 20 75 3B 86 F2 E8
echo e120
echo 3C 00 86 F2 3C 20 74 04 3C 0D 75 2C 32 FF B4 02
echo e130
echo CD 10 EB 24 32 FF B4 03 CD 10 8A C6 8A CA E8 38
echo e140
echo 00 B2 20 B4 02 CD 21 8A C1 E8 2D 00 B2 0D B4 02
echo e150
echo CD 21 B2 0A B4 02 CD 21 32 C0 B4 4C CD 21 32 E4
echo e160
echo AC 3C 20 74 FB 3C 30 72 0D 3C 39 77 09 2C 30 D5
echo e170
echo 0A 8A E0 AC EB EF 8A F4 C3 D4 0A 05 30 30 8B D0
echo e180
echo 80 FC 30 74 08 86 D6 B4 02 CD 21 8A D6 B4 02 CD
echo e190
echo 21 C3
echo rcx
echo 92
echo w
echo q
) | debug textpos.com > nul

例如:

textpos 0 0
colormsg %Red% "Red message at top left corner of the screen"
textpos 10 40
colormsg %Blue%%LightYellow% "This message start in line 10 column 40"

编辑

我稍微修改了TEXTPOS程序,如果不带参数执行,它会显示当前光标位置;这个功能可以让你将光标位置存储在一个变量中,操作如下:textpos > pipe.txt & set /P pos=< pipe.txt(因为textpos | set /P pos=存在错误,不能正常工作)。

如果使用相同的背景和前景颜色显示彩色消息,文本将无法在屏幕上显示;这个特性可以用于输入密码。例如,这是getpassword.bat文件:

@echo off
set password=
for /L %%i in (1,1,%1) do set password=!password!x
colormsg 7 "Enter password: "
textpos > pipe.txt & set /P passpos=< pipe.txt
colormsg 77 "%password%"
textpos %passpos%
set password=
for /L %%i in (1,1,%1) do (
    getakey
    set password=!password!!errorlevel!
    colormsg 7F "*"
)

之前的批处理文件可以读取一个给定字符数的密码,例如:call getpassword 8;输入的字符会被其ASCII代码替换,因此密码具有基本的加密功能。例如,要检查输入的密码是否为“Pass”:

call getpassword 4
if %password% == 8097115115 goto right_password

我之前在一个问题中提供了GETAKEY.COM程序,但这里再提供一次:

@echo off
(
echo e100
echo B4 08 CD 21 B4 4C CD 21
echo rcx
echo 8
echo w
echo q
) | debug getakey.com > nul

如果投票者能够解释一下投反对票的原因,我将不胜感激... :( 请注意,此解决方案是在2011年10月发布的,当时还没有64位版本的Windows,而且这种方法在所有计算机上都可以正确运行! - Aacini
我建议您在回答中明确说明调试命令仅适用于32位Windows版本,这样可以避免愤怒的反应。使用debug是一个好主意,但是通过shell脚本动态生成二进制文件以服务于shell脚本应该始终是最后的选择。您还可以使用PowerShell来生成NE/PE/PE32/PE32+可执行文件来调整您的答案。但是,如果采用这种方式,最好直接使用PowerShell来完成工作 - Zilog80
关于64位Windows的另一个要点是,第一款64位Windows 2003服务器和Windows XP 64位于2005年4月发布。在2011年,许多Windows技术人员已经使用了64位CPU+OS,就像我一样。事实上,我是一个64位的双重LFS/Windowsian。今天,我是一个64位三重Debianist/Androindian/Windowsian。 - Zilog80

3

把这段代码保存为.bat(它是自编译的.net混合代码):

@if (@X)==(@Y) @end /* JScript comment
@echo off
setlocal

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.ForegroundColor;
var backgroundColor = Console.BackgroundColor;
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 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 = 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 = backgroundColor ;
Console.ForegroundColor = foregroundColor ;

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

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

}

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

3
@echo off

REM You can do it easily by typing this at the bottom of your file.

:[any color]
powershell -Command Write-Host "%*" -foreground "[any color]" -background "[any color]"

:[any color]
powershell -Command Write-Host "%*" -foreground "[any color]" -background "[any color]"

REM for typing text in different colors you just need to write this

call :[any color] "[any message]"
call :[any color] "[any other message]"
pause

exit

这个答案有什么问题吗? 它至少是有效的 - 我想知道为什么它被踩了一下 - 这里有什么缺陷? - Gareth Jones

3

你只需要使用一点命令就可以尝试这个:

@echo off
chcp 437>nul&&graftabl 936>nul
ren %WinDir%\System32\config.nt config.nt.bak 2>nul
<"%~f0" more +6 >%WinDir%\System32\config.nt
command /cecho [1;31mHel[32mlo [33mHow[35mare[36myou[m
pause>nul&exit
DOSONLY
dos=high, umb
device=%SystemRoot%\system32\himem.sys
DEVICE==%SystemRoot%\System32\ANSI.SYS /x
files=40

2

将其保存为colormsg.bat

@echo off
if "%~3" == "" goto usage
powershell -command write-host -foreground "%~1" -background "%~2" -nonewline "%~3"
exit /b
:usage
echo.
echo Usage: call colormsg foreground background "message"
echo.
echo Examples:
echo call colormsg white blue "example1"
echo call colormsg 4 2 "example2"

例子:

@echo off
for /f %%a in ('"prompt $H & for %%a in (-) do rem"') do set "BS=%%a"
call colormsg blue 0 "Hello"
<nul set /p "=.%BS% "
call colormsg green 0 "How are you?"
echo.
pause

1

您可以尝试这个简单的脚本。它不使用临时文件。请确保您在“%path%”环境变量中列出的任何文件夹中都有“debug.exe”可执行文件。

@echo off
rem Script written by BrendanSilva [bl8086]
rem You need DEBUG.EXE executable in your system.
setlocal enabledelayedexpansion
set /a _er=0
set /a _n=0
set _ln=%~4
goto init
:howuse ------------------------------------------------------------------------
    echo.
    echo ECOL.BAT - v2.0
    echo Print colored text as batch script without temporary files.
    echo Written by bl8086
    echo.
    echo Syntax:
    echo ECOL.BAT [COLOR] [X] [Y] "Insert your text here"
    echo COLOR value must be a hexadecimal number like "color /?" information
    echo.
    echo Example:
    echo ECOL.BAT F0 20 30 "640K ought to be enough for anybody."
    echo.
    goto :eof
:error ------------------------------------------------------------------------
    set /a "_er=_er | (%~1)"
    goto :eof
:gcnvhx ------------------------------------------------------------------------
    set _cvhx=
    set /a _cvint=%~1
:cnvhx
    set /a "_gch = _cvint & 0xF"
    set _cvhx=!nsys:~%_gch%,1!%_cvhx%
    set /a "_cvint = _cvint >> 4"
    if !_cvint! neq 0 goto cnvhx
    goto :eof
:init --------------------------------------------------------------------------
    if "%~4"=="" call :error 0xff
    (
        set /a _cl=0x%1
        call :error !errorlevel!
        set _cl=%1
        call :error "0x!_cl! ^>^> 8"
        set /a _px=%2
        call :error !errorlevel!
        set /a _py=%3
        call :error !errorlevel!
    ) 2>nul 1>&2
    if !_er! neq 0 (
        echo.
        echo ERROR: value exception "!_er!" occurred. Check memory out.
        echo.
        goto howuse
    )
    set nsys=0123456789ABCDEF
    set /a cnb=0
    set /a cnl=0
    set _cvhx=0
    set _cvint=0
    set _cvmhx=0
:parse -------------------------------------------------------------------------
    set _ch=!_ln:~%_n%,1!
    if "%_ch%"=="" goto perform
    set /a "cnb += 1"
    if %cnb% gtr 7 (
        set /a cnb=0
        set /a "cnl += 1"
    )
    set bln%cnl%=!bln%cnl%! "!_ch!" %_cl%
    set /a "_n += 1"
    goto parse
:perform -----------------------------------------------------------------------
    set /a "in = ((_py * 0xA0) + (_px << 1)) & 0xFFFF"
    call :gcnvhx %in%
    set ntr=!_cvhx!
    set /a jmp=0xe
    set bl8086str=echo.h 0 0
    @for /l %%x in (0,1,%cnl%) do (
        set bl8086str=!bl8086str!^&echo.eb800:!ntr! !bln%%x!
        set /a "in=!jmp! + 0x!ntr!"
        call :gcnvhx !in!
        set ntr=!_cvhx!
        set /a jmp=0x10
    )
    (
    echo %bl8086str%
    echo.q
    ) |debug >nul 2>&1

这个脚本可以将你的文本写在任何屏幕位置。同时可以使用任何颜色。


debug.exe 只能在 32 位的 Windows 上使用。 - phuclv

1
是的,使用 cmdcolor 是可能的:
echo \033[94mHello
echo \033[92mHow are you

Windows 10的conhost支持使用ANSI序列设置颜色,无需使用任何其他第三方工具。 - phuclv

0
使用PowerShell作为子进程,通过命名管道与控制台输出相关联,是一种快速而高效的颜色替代方案,自Windows XP以来。也可以使用FindStr,但PowerShell提供了更多选项,似乎更快。
保持PowerShell作为子进程,并使用管道进行通信的主要优势在于显示要比每行显示时启动PowerShell或FindStr要快得多。
其他优点有:
  • 无需临时文件
  • 通过管道回显允许显示完整的ASCII表格,而不必担心转义。
  • 在fd重定向中可以很好地工作。例如,仅对stderr着色,或重定向到文件/其他进程。
以下是一个示例代码:
::
:: 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

这段“代码”是使用延迟扩展开启编写的,但可以重写以在不使用延迟扩展的情况下工作。有许多安全要点需要考虑,在野外不要直接使用。

如何使用:

@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

链接到我在同一主题上的原始答案。


0

对于Windows 10,当在同一屏幕上显示具有多种颜色的文本时,有一个更简单的选项来处理颜色。

虚拟终端序列 允许控制颜色、光标位置和属性以及屏幕缓冲区。

下面的宏简化了它们的重复使用。

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

:# enable macro
Setlocal EnableExtensions EnableDelayedExpansion

:# facilitate testing of the macro using parameters from the command line
 if not "%~1" == ""  (
  %$Cout% %*
  Exit /B !Errorlevel!
 )

:# usage examples
 %$Cout% /help

 CLS
 %$Cout% /C 31,4,48;2;0;120;180 /S hello world /Y 5 /X 10 /H -
timeout /t 1 > nul
 %$Cout% /Alt /C 32 /Y 6 /X 16 /S Goodbye
timeout /t 1 > nul
 %$Cout% /Main /L 6 /I 28 /C 7,1,90,48;2;150;0;170 /S " again my friends around the "
timeout /t 1 > nul

:# Animation examples

 For /L %%i in (1 1 34)Do (
  For /L %%. in (1 1 20)Do Call :_FalseLabel_Delay 2> nul
  %$Cout% /X 10 /Z 1
 )
 For %%L in (G o o d b y e " ")Do (
  For /L %%. in (1 1 40)Do Call :_FalseLabel_Delay 2> nul
  %$Cout% /I 1 /C 31 /S "%%~L"
 )
 %$Cout% /H + /N
 pause
Goto :eof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接