批处理–如何正确分配可能包含引号的任意百分号(%1)或百分号(%*)参数? [英] Batch – how to correctly assign arbitrary percent-number (%1) or percent-asterisk (%*) arguments that may contain quotes?

查看:99
本文介绍了批处理–如何正确分配可能包含引号的任意百分号(%1)或百分号(%*)参数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在处理上一个问题时,我遇到了最初要解决的类似问题.

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

The call redirections itself works, but in the func itself you can't access the%1anymore.
But there is one solution to grab the output, with a
FOR` loop

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆