Package - how to properly assign arbitrary arguments percent-number (% 1) or percent-asterisk (% *), which may contain quotation marks?

While working on my previous question, I ran into a similar problem that I was solving initially.

In fact, I spent this month trying to create one universal .bat helper library that will handle drag-and-drop files with any names as possible. I want to make this transparent to the caller and backward compatible with any existing scripts, so the file names are passed as% 1,% 2, and I have no problem with this.

@MCND's solution is to read %CMDCMDLINE% and then a complex analysis. Therefore, I can simply write as set "a=%CMDCMDLINE:"=?%" - and have a full line in variable A, but all the quotes inside are instantly replaced by question marks ? set "a=%CMDCMDLINE:"=?%" in an incorrect line, which I can easily process further .

But it only works for .bat drag-and-drop, since I cannot read the parameters passed from the command line. I have to use %* .

My current question is: how to read it if the parameters can also contain quotation symbols, even unpaired?

Consider the following BAT file: (allows you to 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 

Basically, this is all I can imagine to read the incoming packet ("call") percent parameters: forms %1 , %~1 , %* in an unquoted set x=y , quoted by set "x=y" and explicitly quoted set x="y" variations. Deferred extension is just a safe echo value (I don’t care about the ratings here ! In the parameter for simplicity).

My ultimate goal is to create a script that will never cause an error: therefore, all possible errors should appear either before my code receives execution (in the CMD hint itself) or after its completion (when the calling user tries to access the file with the broken name).

So, in this example, there should ideally be no errors between START and DONE, but they will appear in the “given” instructions before the corresponding “echo”.

Attempt # 1

Command: test

Output:

 START a=[] b=[""] c=[] d=[] e=[""] f=[] g=[] h=[""] i=[] DONE 

Good, not errors with null arguments. Let's get:

Attempt # 2

Command: test 123 "56"

Output:

 START a=[123] b=["123"] c=[123] d=[123] e=["123"] f=[123] g=[123 "56"] h=["123 "56""] i=[123 "56"] DONE 

It seems that for "normal" strings, any method works without errors.

Attempt # 3

Command: test ^&-

Output:

 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 

As you can see, cases A, D and G failed ( =%1 , =%~1 and =%* ).

Attempt # 4

Command: test "&-"

Output:

 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 

Now, cases B, C, D, H and I fail ( ="%1" , "=%1" , =%~1 , ="%*" and "=%*" ).

Attempt # 5

Command: test ""^&-"

Output:

 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 

The remaining two cases E and F ( ="%~1" and "=%~1" ) failed to complete together with A and G ( =%1 and %* ).

Thus, none of the “installed” options will work reliably in all attempts to break my code!

Did I skip some other approach for reading the current parameters that come from an untrusted source?

Is it possible to get %… in the normal variable %…% ? Is it possible to replace the quotation marks " directly in %… ? Can I put %* in some special context where things like ampersand and & may not cause changes in execution? Is there any other way to get the arguments (call CMD again, using temporary files - anything?)

Im fine, even if it destroys the original string in these cases (unpaired quotes, unshielded special effects, etc.), but I need to get rid of the parsing error (causing uncontrolled code execution)!

+5
source share
1 answer

You cannot solve this with a normal percentage increase!
Evidence:
An example of the argument "&"& .
To invoke a batch file with this argument, you need to avoid the second ampersand.

 test.bat "&"^& 

It is not possible to handle this with regular commands, since you always get one unordered ampersand.

 set arg=%1 set "arg=%1" set arg=%~1 set "arg=%~1" 

Each line will fail with an error message.

Is there any command that won't work with %1 or %* ?
Yes, REM can handle them properly.

It works without erros.

 REM %1 

...

Funny, but how do you have any benefits of using the REM instruction to get the content?
First you need to enable ECHO ON to see that it really works.

 echo on REM %1 

Now you see the debug output

c: \ temp> REM "&" &

Now you need to redirect the debug output to a file.
There are many ways that do not work.

 @echo on > arg.txt REM %1 ( REM %1 ) > arg.txt call :func > arg.txt .. :func REM %1 

The call redirections itself works, but in the func itself you can't access the cycle 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
anymore.
But there is one solution to grab the output, with a
anymore.
But there is one solution to grab the output, with a
FOR`

 ( @echo on for %%a in (42) do ( rem %1 ) ) > arg.txt 

Is it better to store the argument in a file instead of %1 ?
Yes, since the file can be read in a safe manner, regardless of content.

Some corrections remain in the argument, such as /? , ^ or %%a .
But all this can 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!' 

Read more about the topic at
SO: How to get even the strangest command line options?
SO: Get multi-line arguments

And for Drag & Drop, you need an even more complicated SO solution : drag and drop a batch file for multiple files?

Edit: solution for %*
Extensions must be disabled, otherwise arguments like %a--%~a will be changed.
But without extensions, the %* extension no longer works.
Thus, extensions allow you to simply expand %* , but before FOR displays the contents, 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!' 

This can safely use all the arguments, but for arguments from drag and drop operations even this is not enough, because there is an error in the windows (design defect).
A file like Documents&More cannot be selected this way.

+5
source

Source: https://habr.com/ru/post/1269104/


All Articles