Can't parse arguments to variable

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
WiVi71
Posts: 19
Joined: 06 Jan 2020 02:33

Can't parse arguments to variable

#1 Post by WiVi71 » 12 Apr 2020 11:45

I am making a script that parses

Code: Select all

/SET1 OP1 OP2 /SET2 OP3
to

Code: Select all

/SET1 "WORD" OP1 OP2
/SET2 "WORD" OP3
The /SET... always starts with /Set and does not include double quotes.
and /OP... may or may not contain double quotes and multiple spaces.

This is the code I made to parse the arguments into variables.

Code: Select all

@ECHO OFF
:: Also, This code is a part of and script.
SETLOCAL ENABLEDELAYEDEXPANSION

SET "OP=%~1 "JobName""
IF /I NOT "!OP:~0,4!" == "/Set" (
	ECHO Unrecognized option: "%~1"
	ENDLOCAL & EXIT /B 1
)
IF NOT [%1] == [] SETLOCAL DISABLEDELAYEDEXPANSION & GOTO ARGLoop

:START
ECHO DONE
PAUSE
EXIT /B

:ARGLoop
SHIFT
IF [%1] ==[] (
	PROGRAM %OP% 2> NUL || ECHO Command Failed: "PROGRAM %OP%" 
	ENDLOCAL & GOTO START
)
SET "ARG=%1"
IF /I "%ARG:~0,4%" == "/Set" (
	PROGRAM %OP% 2> NUL || ECHO Command Failed: "PROGRAM %OP%"
	SET "OP=%1 "JobName""
) ELSE (SET "OP=%OP% %1")
GOTO ARGLoop
This code can parse

Code: Select all

/SetHttpMethod OPTIONS /SetMaxDownloadTime 10
into

Code: Select all

/SetHttpMethod "JobName" OPTIONS
/SetMaxDownloadTime "JobName" 10
However, this code cannot parse the arguments below.

Code: Select all

/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1" /SetHttpMethod OPTIONS /SetMaxDownloadTime 10
This problem occurs when the IF or SET command is processing spaces or double quotes.
I can't find a better way to parse arguments into variables without error.
Is there another good way to parse arguments into variables?

pieh-ejdsch
Posts: 240
Joined: 04 Mar 2014 11:14
Location: germany

Re: Can't parse arguments to variable

#2 Post by pieh-ejdsch » 12 Apr 2020 16:32

The comparison with your IF line does not work out as you would expect.
If you want to know what goes wrong then you have to evaluate the displayed errors.
For successful troubleshooting, it has proven useful to switch on the echo and thus to find something in the last displayed lines that leads to the error.
The three quotation marks on the left comparison page cause an incorrect interpretation when executing.

WiVi71
Posts: 19
Joined: 06 Jan 2020 02:33

Re: Can't parse arguments to variable

#3 Post by WiVi71 » 13 Apr 2020 13:45

pieh-ejdsch wrote:
12 Apr 2020 16:32
The comparison with your IF line does not work out as you would expect.
If you want to know what goes wrong then you have to evaluate the displayed errors.
For successful troubleshooting, it has proven useful to switch on the echo and thus to find something in the last displayed lines that leads to the error.
The three quotation marks on the left comparison page cause an incorrect interpretation when executing.
Thanks for the answer.

After a few attempts, I thought there was a limit to comparing strings containing complex double quotes or parentheses using IF command without error.
For example, IF "/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1"" == "" Command... didn't work.

So, I tried to write code that doesn't compare strings with IF commands as much as possible.

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
IF NOT [%3] == [] (
	SET "OP=%~3 "JobName""
	IF /I NOT "!OP:~0,4!" == "/Set" (
		ECHO Unrecognized option: "%~3"
		ENDLOCAL & EXIT /B 1
	)
)
IF NOT [%3] == [] SETLOCAL DISABLEDELAYEDEXPANSION & GOTO ARGLoop

:Start
ECHO DONE
EXIT /B

:ARGLoop
SHIFT /3
SET "ARG=%3"
IF NOT DEFINED ARG GOTO LastArg
:: ECHO "%3" |>NUL FINDSTR /Bi "\"/Set"
:: IF %ERRORLEVEL%  EQU 0 REM This method is safe, but slow..
SET "ARG=%~3"
SET "ARG=%ARG:"=%"
IF /I "%ARG:~0,4%" == "/Set" GOTO EndofArg
:: This method is fast, but can't detect if a double quote is included.
SET "OP=%OP% %3"
GOTO ARGLoop

:LastArg
PROGRAM %OP% || ECHO Command Failed: "PROGRAM %OP%"
ENDLOCAL & GOTO Start

:EndofArg
PROGRAM %OP% || ECHO Command Failed: "PROGRAM %OP%"
SET "OP=%~3 "JobName""
GOTO ARGLoop
Thanks to pieh-ejdsch for answering this question. :D

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Can't parse arguments to variable

#4 Post by penpen » 14 Apr 2020 03:51

WiVi71 wrote:
13 Apr 2020 13:45
After a few attempts, I thought there was a limit to comparing strings containing complex double quotes or parentheses using IF command without error.
For example, IF "/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1"" == "" Command... didn't work.

So, I tried to write code that doesn't compare strings with IF commands as much as possible.
The reason, IF "/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1"" == "" Command... didn't work is that characteres like spaces or semicolons break a token in parts; so you use more than one token for the left comparand (8 in total: '"/SetCustomHeaders "User-Agent:', 'Opera/9.60', '(Windows', 'NT', '6.0', 'U', 'en)', 'Presto/2.1.1""'), which is not allowed because the if-command expects exactly one token.

You could double the doublequotes, so the tokens will not be ripped apart:

Code: Select all

@echo off
cls
setlocal enableExtensions disableDelayedExpansion
set "test=/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1""
set ^"test2=%test:"=""%"
if not "%test2%" == "" echo ok
goto :eof
penpen

WiVi71
Posts: 19
Joined: 06 Jan 2020 02:33

Re: Can't parse arguments to variable

#5 Post by WiVi71 » 14 Apr 2020 14:58

penpen wrote:
14 Apr 2020 03:51
WiVi71 wrote:
13 Apr 2020 13:45
After a few attempts, I thought there was a limit to comparing strings containing complex double quotes or parentheses using IF command without error.
For example, IF "/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1"" == "" Command... didn't work.

So, I tried to write code that doesn't compare strings with IF commands as much as possible.
The reason, IF "/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1"" == "" Command... didn't work is that characteres like spaces or semicolons break a token in parts; so you use more than one token for the left comparand (8 in total: '"/SetCustomHeaders "User-Agent:', 'Opera/9.60', '(Windows', 'NT', '6.0', 'U', 'en)', 'Presto/2.1.1""'), which is not allowed because the if-command expects exactly one token.

You could double the doublequotes, so the tokens will not be ripped apart:

Code: Select all

@echo off
cls
setlocal enableExtensions disableDelayedExpansion
set "test=/SetCustomHeaders "User-Agent: Opera/9.60 (Windows NT 6.0; U; en) Presto/2.1.1""
set ^"test2=%test:"=""%"
if not "%test2%" == "" echo ok
goto :eof
penpen
Thanks for the answer. Comparing the arguments using FINDSTR was slow to use, but the above method solved the speed problem.
This is the completed code with reference to the code above.

Code: Select all

@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
IF "%~2" == "" (
	ECHO USAGE: BITS_DN.CMD "<URL>" "<FILE>" [OPTIONS]
	ENDLOCAL & EXIT /B 1
)
IF NOT [%3] == [] (
	SET ^"OP=%3 "JobName""
	IF /I NOT "!OP:~0,4!" == "/Set" (
		ECHO Unrecognized option: "%~3"
		ENDLOCAL & EXIT /B 1
	)
)

::.....
IF NOT [%3] == [] SETLOCAL DISABLEDELAYEDEXPANSION & GOTO ARGLoop

:Start
ECHO DONE
EXIT /B

:ARGLoop
SHIFT /3
SET ^"ARG=%3"
SETLOCAL ENABLEDELAYEDEXPANSION
IF NOT DEFINED ARG (
	ECHO %DATE% %TIME% [ERROR] - Command Failed: "PROGRAM !OP!"
	ENDLOCAL & ENDLOCAL & GOTO Start
)
SET ^"ARG=!ARG:"=####!"
IF /I "!ARG:~0,4!" == "/Set" (
	ECHO %DATE% %TIME% [ERROR] - Command Failed: "PROGRAM !OP!"
	ENDLOCAL
	SET "OP=%3 "JobName""
	GOTO ARGLoop
)
ENDLOCAL
SET ^"OP=%OP% %3"
GOTO ARGLoop
In your code above, Unlike SET "W=...", SET ^"W=..." worked well even with double quotes.
For example, SET "W="&"" does not work but SET ^"W="&"" works, and also SET ^"W="&" too.

I wonder how does SET ^" works and if there is a case that causes an error unlike SET "W=..."
Is there any page I can refer?

penpen
Expert
Posts: 2009
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Can't parse arguments to variable

#6 Post by penpen » 16 Apr 2020 01:28

I silently assumed you know all special characters (see last line in command: cmd /?).
The command line intrepreter knows special characters, like the CIRCUMFLEX ACCENT (^) which is an escape character that treats the following character as a normal (=not special) character.
The QUOTATION MARK (") is also a special character, but it escapes all the following characters as normal characters up to the next QUOTATION MARK.
So if you escape the QUOTATION MARK (also called doublequotes), then that QUOTATION MARK does not escape the following characters.
I escaped the first doublequotes in the set command, because there are an uneven number of them in that line, which might cause some havoc, so i escaped one of them (which one to escape also depends on your data):

Code: Select all

set ^"test2=%test:"=""%"
Sidenote: You might to play with them and escape different ones to see the effects when escaping other doublequotes.

You shouldn't use the escape the doublequotes unless you really need that.

I would suggest you to per default using doublequotes, when using the commands set, if and around filenames, so you can be sure that all special characters are escaped (unless you are using no doublequotes between: In that case you could double them as shown above):

Code: Select all

set "test=a b c"
if "a b c" == "%test%" >"testFile.txt" echo(Test.
I also would suggest you to always use the same syntax for the same commands, unless you have a really good reason to do otherwise (in which case you should add a comment in the final batch file, so if you or another one is reading that later you know why you did otherwise).


penpen

Post Reply