在处理我的previous问题时,我遇到了一个我最初解决的类似问题。
实际上,本月我花了一些时间尝试创建一个通用的.bat帮助程序库,它将尽可能地使用任何名称处理拖放文件。我想对调用者透明地执行它,并向后兼容任何现有脚本,因此文件名以%1,%2的形式传递,我对此没有任何问题。
@ MCND的解决方案是阅读%CMDCMDLINE%
,然后狡猾地解析它。所以我可以简单地写成set "a=%CMDCMDLINE:"=?%"
- 并将完整的字符串放入变量A中,但内部的所有引号都会立即替换为问号?
,从而产生一个我可以轻松处理的不带引号的行。 / p>
但它仅适用于.bat拖放,因为这样我无法读取从命令提示符传递的参数。我必须使用%*
。
我目前的问题是:如果参数也可能包含引号字符,甚至不配对,如何阅读?
考虑以下BAT文件:(让我们打电话给#34; 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"
{1}},引用set x="y"
并明确引用!
种变体。延迟扩展只是为了安全地回显值(这里我不关心参数中的检查标记test
)。
我的最终目标是创建一个永远不会抛出错误的脚本:因此每个可能的错误都应该出现在我的代码执行之前(在CMD提示本身),或者在完成之后(当调用者尝试访问时)名字坏了的文件。)
所以在这个例子中,在START和DONE之间理想情况下应该没有错误,但它们会出现在" set"说明,在相应的" echo"。
之前尝试#1
命令:START
a=[]
b=[""]
c=[]
d=[]
e=[""]
f=[]
g=[]
h=[""]
i=[]
DONE
输出:
test 123 "56"
好,不是零参数的错误。我们得到一些:
尝试#2
命令:START
a=[123]
b=["123"]
c=[123]
d=[123]
e=["123"]
f=[123]
g=[123 "56"]
h=["123 "56""]
i=[123 "56"]
DONE
输出:
test ^&-
看起来像#34;正常"字符串任何方法都可以正常工作。
尝试#3
命令: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
输出:
=%1
如您所见,案例A,D和G失败(=%~1
,=%*
和test "&-"
)。
尝试#4
命令: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
输出:
="%1"
现在案例B,C,D,H和我都失败了("=%1"
,=%~1
,="%*"
,"=%*"
和test ""^&-"
)。
尝试#5
命令: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
输出:
="%~1"
其余两个案例E和F("=%~1"
和=%1
)以及A和G(%*
和%…
)失败。
所以,没有一个"设置"变种将在所有破坏我的代码的尝试中可靠地工作!
我是否缺少其他方法来读取来自不受信任来源的当前参数?
我可以在正常变量%…%
中获得"
吗?我可以直接在%…
中替换引号%*
吗?我可以将&
置于某些特殊情况下,例如&符号{{1}}可能不会导致执行更改吗?是否有另一种获取参数的方法(再次调用CMD,使用临时文件 - 什么?)
我很好,即使在这些情况下它会破坏原始字符串(不成对的引号,未转义的特殊内容等),但我需要摆脱解析错误(导致不受控制的代码执行)!
答案 0 :(得分:5)
您无法通过正常百分比扩展解决问题!
证明:
样本参数为"&"&
要使用此参数调用批处理文件,您必须转义第二个&符号。
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 anymore.
FOR`循环
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:How to receive even the strangest command line parameters?
SO:Receive multiline arguments
对于Drag& Drop,您需要一个更复杂的解决方案SO:Drag and drop batch file for multiple files?
编辑:%*
的解决方案
必须禁用扩展名,否则将修改%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!'
这可以以安全的方式获取所有参数,但是对于来自拖放操作的参数,即使这还不够,因为窗口有一个错误(设计缺陷)。
像Documents&More
这样的文件无法通过这种方式获取。