2017-06-22 39 views
5

在处理我的previous问题时,我遇到了一个类似的问题,我最初正在解决这个问题。批处理 - 如何正确分配可能包含引号的任意百分号(%1)或百分号(%*)参数?

实际上,本月我花了一个试图创建一个通用.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之间理想情况下应该没有错误,但是它们会在相应的“回声”之前出现在“设置”指令处。

尝试#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 

无法与A和G沿其余2案件E和F(="%~1""=%~1")(=%1%*)。

因此,没有任何“set”变种可以在所有试图破坏我的代码的情况下可靠地工作!

我是否缺少一些其他方法来读取来自不可信源的当前参数?

我可以在正常变量%…%中获得%…吗?我可以在%…中直接替换报价"吗?我可以将%*放在某些特殊的情况下,例如&符号&可能不会导致更改执行吗?是否有另一种方法来获取参数(再次调用CMD,使用临时文件 - 任何东西?)

即使它会在这些情况下销毁原始字符串(未成对的引号,未转义的特价等),但我很好,但我需要摆脱解析错误(导致不受控制的代码执行)!

+0

尝试#5:尝试改变'测试“”^& - “'测试”“^& - ^”'...... – aschipfl

回答

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.
But there is one solution to grab the output, with a
FOR`环

(
    @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

而对于将&放弃你需要一个更复杂的解决方案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!' 

这可以在一个安全的方式获取所有参数,但是从一拖&拖放操作参数,即使这是不够的,因为Windows有一个bug(设计缺陷)。
Documents&More这样的文件不能以这种方式获取。

+0

哇,着名的Jeb!我试过你的方法,它的工作。直到我传递像'; =,'这样的东西,因为这些是分隔符,如果没有引用,就根本不会传递给%1。也很难(我想)得到参数数(并且提取它们全部,不仅是首先)。是否有%*的解决方案(由于禁用扩展而不能在这里工作),尽可能少的副作用?我有我自己的尝试,我测试了它,并找不到任何休息;所有作品('/?','%〜...','^' - 还有什么麻烦?)。你能看一下吗?我只是将它发布在以下注释中: – aleksusklim

+0

'因为它是多行的,从这里复制完成后用newlines替换所有'#'。这里有“退格”字符,用'$'代替 - 你还必须用“7Fh”字符替换它(Ctrl + Backspace in Notepad或Alt + 0127):'#@ setlocal enableextensions disabledelayedexpansion#@ echo on&@ (for(%)$ in(_)do(@ call#rem @%* @#))> tmp.txt&@echo off&endlocal ## set“!=”&set“&=”&for/F“tokens = 1 * delims = @“%% G in(tmp.txt)DO set”!= %% H“&if defined defined!设置“&= %% H”#set“&=%&:”= $%“#set”&=%&:〜1,-2%“#echo”%&%“#'。变量可以防止扩展;也可以用作引用替换 – aleksusklim

+0

@aleksusklim它会失败,参数例如'%$ - %〜$',请参阅我编辑的答案我不理解部分_“使用backspace作为变量可防止扩展“_。 – jeb