批处理–如何正确分配可能包含引号的任意百分号(%1)或百分号(%*)参数? [英] Batch – how to correctly assign arbitrary percent-number (%1) or percent-asterisk (%*) arguments that may contain quotes?
问题描述
在处理上一个问题时,我遇到了最初要解决的类似问题.
While working on my previous question, I faced a similar issue that I was solving initially.
实际上,我本月花费了很多时间来尝试创建一个通用的.bat帮助器库,该库将尽可能处理具有任何名称的拖放文件.我想对调用者透明地执行此操作,并与任何现有脚本向后兼容,因此文件名以%1,%2的形式提供,对此我没有任何问题.
Actually, I spent this month in attempts to create one universal .bat helper library that will handle drag-and-dropped files with any names as best as it is possible. I want to do it transparently for the caller, and backward-compatible with any existing scripts, so filenames are delivered as %1, %2, and I have no problems with that.
@MCND的解决方案是读取%CMDCMDLINE%
,然后棘手地解析它.因此,我可以像set "a=%CMDCMDLINE:"=?%"
那样简单地写–并将完整的字符串放入变量A中,但是其中的所有引号都立即被问号?
替换,从而产生了无引号的行,可以轻松地对其进行进一步处理.
Solution of @MCND is to read %CMDCMDLINE%
and then tricky parse it. So I can simply write like set "a=%CMDCMDLINE:"=?%"
– and have the full string into variable A, but all quotes inside are instantly replaced with question marks ?
, resulting in an unquoted line that I can easily process further.
但是它仅适用于.bat拖放操作,因为那样我无法读取从命令提示符传递的参数.我必须使用%*
.
But it works only for .bat drag-and-drop, since that way I can’t read parameters passed from command prompt. I have to use %*
.
我当前的问题是:如果参数中也可能包含引号甚至不成对,该如何读取?
My current question is: how to read it if the parameters may also contain quotation characters, even unpaired?
考虑以下BAT文件:(我们将其命名为"C:\ test.bat")
Consider the following BAT file: (let’s call "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"
变体.延迟扩展只是为了安全地回显该值(为简单起见,这里我并不关心参数中的检查标记!
).
Basically it is everything that I can imagine to read incoming batch ("call") percent-parameters: forms %1
, %~1
, %*
in unquoted set x=y
, quoted set "x=y"
and explicitly quoted set x="y"
variations. Delayed expansion is only to safely echo the value (here I don’t care about examination marks !
in parameter for simplicity).
我的最终目标是创建一个永远不会抛出错误的脚本:因此,每一个可能的错误都应该在我的代码执行之前(在CMD提示符本身处)或在代码完成之后(当调用方尝试访问a)时出现.带有损坏名称的文件).
My final aim is to create a script that will never throw an error: so every possible errors should appear either before my code gets execution (at CMD prompt itself), or after it finished (when the caller will try to access a file with a broken name).
因此,在此示例中,理想情况下,在START和DONE之间应该没有错误,但是它们将在相应的"echo"之前出现在"set"指令中.
So in this example, between START and DONE ideally there should be no errors, but they will appear at "set" instructions, before the corresponding "echo".
尝试#1
命令:test
输出:
START
a=[]
b=[""]
c=[]
d=[]
e=[""]
f=[]
g=[]
h=[""]
i=[]
DONE
好,参数为零时不会出错.让我们得到一些:
Good, not errors at zero arguments. Let’s get some:
尝试#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
就像普通"字符串一样,任何方法都能正常工作.
Looks like for "normal" strings any method works without errors.
尝试#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
和=%*
).
As you can see, cases A, D and G failed (=%1
, =%~1
and =%*
).
尝试#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
,="%*"
和"=%*"
).
Now cases B, C, D, H and I are failed (="%1"
, "=%1"
, =%~1
, ="%*"
and "=%*"
).
尝试#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
和%*
)均未通过.
Failed the rest two cases E and F (="%~1"
and "=%~1"
) along with A and G (=%1
and %*
).
因此,在尝试破坏我的代码的所有尝试中,任何"set"变体都无法可靠地工作!
So, none of "set" variants will work reliably in all attempts to break my code!
我还缺少其他一些方法来读取来自不受信任来源的当前参数吗?
Am I missing some other approach to read current parameters that come from untrusted source?
我可以在普通变量%…%
中获得%…
吗?我可以直接在%…
中替换引号"
吗?我可以将%*
放在某些特殊的上下文中,例如&符号&
可能不会导致执行更改吗?还有另一种获取参数的方法(再次调用CMD,使用临时文件-什么?)
Can I get %…
in a normal variable %…%
? Can I replace quotes "
directly in %…
? Can I put %*
in some special context where things like ampersand &
may not cause change of execution? Is there another way to get arguments (calling CMD again, use temporary files – anything?)
即使在这种情况下它会破坏原始字符串(未配对的引号,未转义的特殊字符等),我也很好,但是我需要摆脱解析错误(导致不受控制的代码执行)!
I’m fine even if it will destroy the original string in these cases (unpaired quotes, unescaped specials, etc.), but I need to get rid of parse error (causing uncontrolled code execution)!
推荐答案
您无法通过 normal 百分比扩展来解决它!
证明:
示例参数为"&"&
.
要使用此参数调用批处理文件,必须转义第二个&符号.
You can't solve it with normal percent expansion!
Proof:
The sample argument is "&"&
.
To call a batch file with this argument, you have to escape the second ampersand.
test.bat "&"^&
使用常规命令无法处理此问题,因为您总是会得到一个不带引号的&"号.
It's not possible to handle this with normal commands, as you always get one unquoted ampersand.
set arg=%1
set "arg=%1"
set arg=%~1
set "arg=%~1"
每行将失败,并显示一条错误消息.
Each line will fail with an error message.
是否有不会因%1
或%*
而失败的命令?
是的,REM
可以正确处理它们.
Is there any command that will not fail with %1
or %*
?
Yes, REM
can handle them proper.
这没有错误.
REM %1
...
好笑,但是使用REM
语句获取内容有什么好处?
首先,您必须启用ECHO ON
才能看到它确实有效.
Funny, but how do you have any benefits from using a REM
statement to get the content?
First you have to enable ECHO ON
to see that it really works.
echo on
REM %1
现在您可以看到调试输出了
Nice now you see the debug output
c:\ temp> REM&"&
c:\temp>REM "&"&
现在,您只需要将调试输出重定向到文件.
有很多方法行不通.
Now you only need to redirect the debug output to a file.
There are many ways that doesn't work.
@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
The call redirections itself works, but in the func itself you can't access the
%1anymore.
FOR` loop
But there is one solution to grab the output, with a
(
@echo on
for %%a in (42) do (
rem %1
)
) > arg.txt
将参数存储在文件中而不是%1
更好吗?
是的,因为可以独立于内容安全地读取文件.
Is it better to store the argument in a file instead of %1
?
Yes, as a file can be read in a safe way independent of the content.
参数中还剩下一些陷阱,例如/?
,^
或%%a
.
但这一切都可以解决.
There are still some gotchas left, like /?
, ^
or %%a
in the argument.
But that can all be solved.
@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:接收多行参数
More about the topic at
SO:How to receive even the strangest command line parameters?
SO:Receive multiline arguments
对于拖放操作,您需要更复杂的解决方案 SO:将批处理文件拖放到多个文件中?
And for Drag&Drop you need an even more complex solution SO:Drag and drop batch file for multiple files?
%*
的解决方案
必须禁用扩展名,否则将修改%a--%~a
之类的参数.
但是如果没有扩展名,%*
扩展将无法再使用.
因此,启用扩展名只是为了扩展%*
,但是在FOR
显示内容之前,将禁用扩展名.
Solution for %*
The extensions has to be disabled, else arguments like %a--%~a
will be modified.
But without extensions the %*
expansion doesn't work anymore.
So the extensions are enabled just to expand the %*
but before the FOR
shows the content the extensions will be disabled.
@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
这样的文件.
This can fetch all arguments in a safe way, but for arguments from a drag&drop operations even this isn't enough, as windows have a bug (design flaw).
A file like Documents&More
can't be fetched this way.
这篇关于批处理–如何正确分配可能包含引号的任意百分号(%1)或百分号(%*)参数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!