我无法使用^^!转义感叹号。我知道我需要使用setlocal disabledelayedexpansion或只使用endlocal,但我找不到正确的位置放置它,不会出现任何错误。
这是我的脚本,用于在cmd窗口的水平中心显示文本:

@echo off
setlocal enabledelayedexpansion
title Center Text
mode 80,50

set "cmdwidth=80"
:Display
cls
set Center=This is a test^^! & call :CenterText Center strLen
echo.
pause
exit

:CenterText
  if not "!%1:~%len%!"=="" set /A len+=1 & goto :CenterText
(endlocal & set %2=%len%)

goto CenterTextDisplay
:AddSpace
set "spaces=%spaces% "
goto :eof
:CenterTextDisplay
set /a "indent=(cmdwidth - strLen)/2"
set "spaces= "
for /l %%a in (1,1,%indent%) do call :AddSpace
echo %spaces%%Center%
set "len=0"
goto :eof
这是我的代码,没有错误,但是我不能正确地转义感叹号,结果是This is a test而不是This is a test!

最佳答案

每次启用延迟扩展时,当文字字符串包含感叹号时,以及立即展开将感叹号保留在其字符串值中的变量(常规%扩展)时,感叹号都会丢失(或导致其他意外结果)。 for参数(例如%%I)和参数引用(例如%1)也是如此,因为所有这些参数都在延迟扩展发生之前被扩展了。
为避免任何此类问题,应仅在实际需要时才启用延迟扩展。

在您的代码中,您启用了全局延迟扩展。当您正确地将其转义时,变量Center实际上会保存感叹号,但是一旦在%Center%行中扩展echo %spaces%%Center%,它就会丢失。

这是改编的脚本:

@echo off
setlocal DisableDelayedExpansion
title Center Text
mode 80,50

set "cmdwidth=80"
:Display
cls
set "Center=This is a test!" & call :CenterText Center strLen
echo/
pause
endlocal
exit /B

:CenterText
setlocal EnableDelayedExpansion
:CenterText_Loop
if not "!%~1:~%len%!"=="" set /A len+=1 & goto :CenterText_Loop
endlocal & set "%~2=%len%"
set /a "indent=(cmdwidth-strLen)/2"
set "spaces= "
for /l %%a in (1,1,%indent%) do call :AddSpace
echo(%spaces%%Center%
set "len=0"
goto :eof

:AddSpace
set "spaces=%spaces% "
goto :eof

除了纠正延迟扩展问题外,我还修复了以下问题:
  • exit更改为exit /B,以仅终止批处理脚本,而不终止父cmd实例;
  • echo.更改为echo/,因为如果当前目录中的文件名为echo(无文件扩展名),则前者可能会失败;
  • 通过一致地引用整个赋值表达式来改进了set语法;
  • %1更改为%~1,将%2更改为%~2,以从扩展值中删除潜在的引号引起来;
  • 将子例程:AddSpace向下移动,以阐明执行流程并避免需要标签:CenterTextDisplay和相关的goto
    实际上,如果您用以下命令替换for /l %%a in (1,1,%indent%) do call :AddSpace,甚至可以删除该子例程:
    for /l %%a in (1,1,%indent%) do call set "spaces=%%spaces%% "
    

    这说明了延迟扩展的一种替代方法:将变量周围的%符号加倍,并使用call;但是,这并非在所有情况下都有效,因为某些字符可能仍然会引起麻烦,并且某些命令(例如forif)无法由call运行;
  • 10-08 05:10