嘿伙计我在编码方面相当新,我似乎无法使用^^!
来逃避感叹号。我知道我需要使用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!
。
答案 0 :(得分:2)
每次启用延迟扩展时,当文字字符串包含感叹号时,以及当包含感叹号的变量立即展开(正常%
扩展)时,感叹号会丢失(或导致其他意外结果)在其字符串值;对于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
;但是这并不适用于所有情况,因为某些字符可能仍然会导致问题,for
无法运行某些命令(如if
和call
);