在处理我之前的问题时,我遇到了一个我最初解决的类似问题.
实际上,我本月花了一些时间尝试创建一个通用的.bat帮助程序库,它将尽可能地使用任何名称处理拖放文件.我想对调用者透明地执行它,并向后兼容任何现有脚本,因此文件名以%1,%2的形式传递,我没有遇到任何问题.
@ MCND的解决方案是读取%CMDCMDLINE%
然后棘手解析它.因此,我可以简单地编写set "a=%CMDCMDLINE:"=?%"
- 并将完整的字符串放入变量A中,但内部的所有引号都会立即替换为问号?
,从而导致我可以轻松处理的不带引号的行.
但它只适用于.bat拖放,因为这样我就无法读取从命令提示符传递的参数.我必须使用%*
.
我目前的问题是:如果参数也可能包含引号字符,甚至不配对,如何阅读?
考虑以下BAT文件:(让我们调用"C:\ test.bat")
@echo off
echo START
setlocal enabledelayedexpansion
set a=%1
echo a=[!a!]
set b="%1"
echo b=[!b!]
set "c=%1"
echo c=[!c!]
set d=%~1
echo d=[!d!]
set e="%~1"
echo e=[!e!]
set "f=%~1"
echo f=[!f!]
set g=%*
echo g=[!g!]
set h="%*"
echo h=[!h!]
set "i=%*"
echo i=[!i!]
endlocal
echo DONE
exit /b
基本上,它是一切,我能想象到读取进入批量("呼叫")百分比参数:形式%1
,%~1
,%*
在不带引号set x=y
,引用set "x=y"
并明确援引set x="y"
变化.延迟扩展只是为了安全地回显该值(!
为简单起见,这里我不关心参数中的检查标记).
我的最终目标是创建一个永远不会抛出错误的脚本:因此每个可能的错误都应该出现在我的代码执行之前(在CMD提示本身),或者在完成之后(当调用者尝试访问带有破碎的名字).
因此,在这个例子中,理想情况下,在START和DONE之间不应该有错误,但它们将出现在相应"echo"之前的"set"指令处.
尝试#1
命令: test
输出:
START
a=[]
b=[""]
c=[]
d=[]
e=[""]
f=[]
g=[]
h=[""]
i=[]
DONE
好,不是零参数的错误.我们得到一些:
尝试#2
命令: test 123 "56"
输出:
START
a=[123]
b=["123"]
c=[123]
d=[123]
e=["123"]
f=[123]
g=[123 "56"]
h=["123 "56""]
i=[123 "56"]
DONE
看起来像"普通"字符串任何方法都可以正常工作.
尝试#3
命令: test ^&-
输出:
START
'-' is not recognized as an internal or external command,
operable program or batch file.
a=[]
b=["&-"]
c=[&-]
'-' is not recognized as an internal or external command,
operable program or batch file.
d=[]
e=["&-"]
f=[&-]
'-' is not recognized as an internal or external command,
operable program or batch file.
g=[]
h=["&-"]
i=[&-]
DONE
如您所见,案例A,D和G失败(=%1
,=%~1
和=%*
).
尝试#4
命令: test "&-"
输出:
START
a=["&-"]
'-""' is not recognized as an internal or external command,
operable program or batch file.
b=[""]
'-""' is not recognized as an internal or external command,
operable program or batch file.
c=[]
'-""' is not recognized as an internal or external command,
operable program or batch file.
d=[]
e=["&-"]
f=[&-]
g=["&-"]
'-""' is not recognized as an internal or external command,
operable program or batch file.
h=[""]
'-""' is not recognized as an internal or external command,
operable program or batch file.
i=[]
DONE
现在例B,C,d,H和I失败(="%1"
,"=%1"
,=%~1
,="%*"
和"=%*"
).
尝试#5
命令: test ""^&-"
输出:
START
'-"' is not recognized as an internal or external command,
operable program or batch file.
a=[""]
b=["""&-""]
c=[""&-"]
d=["&-]
'-"' is not recognized as an internal or external command,
operable program or batch file.
e=[""]
'-"' is not recognized as an internal or external command,
operable program or batch file.
f=[]
'-"' is not recognized as an internal or external command,
operable program or batch file.
g=[""]
h=["""&-""]
i=[""&-"]
DONE
其余两种情况E和F(="%~1"
和"=%~1"
)以及A和G(=%1
和%*
)失败.
因此,在所有破坏我的代码的尝试中,"set"变体都不会可靠地运行!
我是否缺少其他方法来读取来自不受信任来源的当前参数?
我可以%…
进入正常变量%…%
吗?我可以"
直接替换引号%…
吗?我可以%*
在某些特殊情况下放置像&符号这样的东西&
可能不会导致执行变更吗?是否有另一种获取参数的方法(再次调用CMD,使用临时文件 - 什么?)
我很好,即使它会破坏这些情况下的原始字符串(不成对的引号,未转义的特价等),但我需要摆脱解析错误(导致不受控制的代码执行)!
1> jeb..:
你无法用正常的百分比扩展来解决它!
证明:
样本参数是"&"&
.
要使用此参数调用批处理文件,您必须转义第二个&符号.
test.bat "&"^&
使用普通命令无法处理此问题,因为您始终会获得一个未加引号的&符号.
set arg=%1
set "arg=%1"
set arg=%~1
set "arg=%~1"
每行都将失败并显示错误消息.
是否有任何命令不会失败%1
或%*
?
是的,REM
可以正确处理它们.
这没有错误.
REM %1
...
有趣,但如何使用REM
声明获取内容有什么好处?
首先,您必须启用ECHO ON
才能看到它确实有效.
echo on
REM %1
好了,现在你看到了调试输出
c:\ temp> REM"&"&
现在您只需要将调试输出重定向到文件.
有很多方法都行不通.
@echo on
> arg.txt REM %1
( REM %1 ) > arg.txt
call :func > arg.txt
..
:func
REM %1
的call redirections itself works, but in the func itself you can't access the
%1个FOR`环anymore.
But there is one solution to grab the output, with a
(
@echo on
for %%a in (42) do (
rem %1
)
) > arg.txt
是否更好地将参数存储在文件中而不是%1
?
是的,因为文件可以独立于内容的安全方式读取.
还有一些陷阱离开,像/?
,^
或%%a
在争论.
但这一切都可以解决.
@echo off
setlocal DisableDelayedExpansion
set "prompt=X"
setlocal DisableExtensions
(
@echo on
for %%a in (4) do (
rem #%1#
)
) > XY.txt
@echo off
echo x
endlocal
for /F "delims=" %%a in (xy.txt) DO (
set "param=%%a"
)
setlocal EnableDelayedExpansion
set param=!param:~7,-4!
echo param='!param!'
有关
SO主题的更多信息:如何接收最奇怪的命令行参数?
SO:接收多行参数
对于Drag&Drop,您需要一个更复杂的解决方案SO:拖放多个文件的批处理文件?
编辑:%*
必须禁用扩展的解决方案,否则%a--%~a
将修改参数.
但是没有扩展,%*
扩展就不再起作用了.
因此,扩展功能只是为了扩展,%*
但在FOR
显示内容之前,扩展将被禁用.
@echo off
setlocal EnableExtensions DisableDelayedexpansion
set "prompt=-"
(
setlocal DisableExtensions
@echo on
for %%a in (%%a) do (
rem # %*#
)
) > args.tmp
@echo off
endlocal
set "args="
for /F "tokens=1* delims=#" %%G in (args.tmp) DO if not defined args set "args=%%H"
setlocal EnableDelayedExpansion
set "args=!args:~1,-4!"
echo('!args!'
这可以以安全的方式获取所有参数,但是对于来自拖放操作的参数,即使这还不够,因为windows有一个bug(设计缺陷).
像这样的文件Documents&More
无法以这种方式获取.