CALL传递时百分号消失

时间:2013-02-08 14:07:04

标签: windows cmd escaping call

此问题最初来自Escape percent signs in given variables。我不想在那里弄清楚好的答案。但我的问题有所改变......

假设有一个由双引号括起来的给定字符串变量,它可能包含一个或多个百分号。永久切换到启用的延迟扩展是不可能的(其他代码已经可用)。调用包含字符串变量的函数作为参数是必要的。这是我到目前为止所确定的:

@echo off & setlocal ENABLEEXTENSIONS
SET AlbumArtist=%1
CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN
echo %AlbumArtist_VDN%

CALL :EscapePoisonChars %%AlbumArtist%% AlbumArtist_VDN
echo %AlbumArtist_VDN%
endlocal &GOTO:EOF

:EscapePoisonChars
@echo off & setlocal ENABLEEXTENSIONS
SET TmpString="%~1"
SET TmpString=%TmpString:&=^^^&%
SET TmpString=%TmpString:(=^^^(%
SET TmpString=%TmpString:)=^^^)%
endlocal&SET %2=%TmpString:~1,-1%&GOTO :EOF

我知道这可能不是一个“干净的解决方案”。但我想了解为什么CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN调用例程时百分号消失。当使用由双倍百分号括起来的字符串变量%%AlbumArtist%%调用时,它会提供所需的输出:

D:\Batch>PercentTwins.bat "100% Rock & Roll"
100 Rock & Roll
100% Rock & Roll

D:\Batch>

如果%AlbumArtist%在函数:EscapePoisonChars之内或之外展开,为什么会有不同的结果?有了echo,我看到百分号随SET TmpString="~1"消失。任何解释都将帮助我改进我的进一步cmd技术。谢谢!

2 个答案:

答案 0 :(得分:1)

如果我错了,任何人都会纠正我,但我认为当一个命令中的单个百分号符号传递给另一个命令时,它们将消失。如果它只是在同一个命令中使用,它通常不会消失。

(可能有更'正确'的说法,或者这个想法可能完全错了)

(有关Microsoft Support website处百分号的一些信息)

答案 1 :(得分:0)

call命令第二次启动%解析阶段(引用问题帖How does the Windows Command Interpreter (CMD.EXE) parse scripts?的已接受答案);这让%标志消失了。您可以提前将%符号加倍,但您需要为此启用delayed expansion,如下所示:

@echo off & setlocal ENABLEEXTENSIONS EnableDelayedExpansion
SET AlbumArtist=%1
set AlbumArtist=!AlbumArtist:%%=%%%%!
CALL :EscapePoisonChars %AlbumArtist% AlbumArtist_VDN
echo %AlbumArtist_VDN%
endlocal &GOTO:EOF

在第二个子例程调用中,两个解析阶段看不到原始字符串的%符号;第一阶段将%%AlbumArtist%%字面扩展为%AlbumArtist%,第二阶段将其扩展为100% Rock & Roll

<强>可是:
当您坚持使用唯一安全的set语法并确保始终正确引用字符串时,根本不需要所有这些,您根本不需要子例程:

set "AlbumArtist=%~1"

%1将返回给定的字符串; %~1删除了潜在的引号。将整个赋值表达式括在引号中会使 not 的语法对于有毒字符具有鲁棒性,将""视为已分配字符串本身的一部分。

当使用/返回字符串时(通过此处示例中的echo),您需要再次将字符串括在引号内以保持安全,以防您使用正常(立即)%扩展:

set "AlbumArtist=%~1"
echo "%AlbumArtist%"

或者,延迟扩展使读取变量即使没有引号也是安全的:

set "AlbumArtist=%~1"
setlocal EnableDelayedExpansion
echo !AlbumArtist!
endlocal