批处理 - 如何正确分配可能包含引号的任意百分号(%1)或百分比星号(%*)参数?

时间:2017-06-22 11:59:27

标签: batch-file

在处理我的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=yset "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,使用临时文件 - 什么?)

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

1 个答案:

答案 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.
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

对于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这样的文件无法通过这种方式获取。