Extracting substring from a batch or function argument

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Extracting substring from a batch or function argument

#1 Post by sambul35 » 25 May 2016 21:22

On SS64 Cmd Syntax Replace page this syntax is given but not explained or illustrated with examples:

A.

Code: Select all

%~[param_ext]$variable:Param

It appears to hint how a substring can be extracted from a batch or function argument. Is that correct?

One way to extract such substring is to assign a variable to a batch or function argument, then extract substring of the variable:

Code: Select all

:: if argument %1="%opt%"
set "var1=%1"
set "var2=%var1:~2,3%"


But it requires changing the variable name. I need to set "opt=5" (i.e. change a value of the "hidden" variable in argument %1), but instead the above substitution only seems to allow setting "var2=5". How would using syntax (A) above may help, or is there any other short way to extract a substring without changing variable name, like ECHO or PIPE?
Last edited by sambul35 on 27 May 2016 14:22, edited 4 times in total.

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

Re: Extracting substring from a function argument

#2 Post by penpen » 26 May 2016 02:01

You could find the example of this special syntax within the section.
"Finding items within the PATH environment variable".

So you cannot extract a substring from a batch or function argument:
The same syntax is used by for-variables; see "help for" on command line.

Your example should be the shortest possible.


penpen

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Extracting substring from a function argument

#3 Post by sambul35 » 26 May 2016 08:18

Ooops, I overlooked the PATH examples, since a lot more of these are given in the dedicated parameters page, though most related to PATH elements extractions. :?

However, my above example expands value of the variable %opt% and then strips it, while I need to extract the "opt" variable name from the function argument (without knowing upfront what that name is), so that "opt" can be assigned a new value (i.e. set "opt=5"). Not sure if FOR is relevant here, since it'll also extract the %opt% value and assign it to %%K rather than treat %opt% as a string. What basic construct would treat the variable %opt% as a string in this example and strip %...% from it?

Squashman
Expert
Posts: 4486
Joined: 23 Dec 2011 13:59

Re: Extracting substring from a batch or function argument

#4 Post by Squashman » 26 May 2016 09:04

Are you looking for an in string function?
viewtopic.php?t=1986

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Extracting substring from a batch or function argument

#5 Post by sambul35 » 26 May 2016 11:55

Of course its possible in some sort of Find String or FOR /F operation though looks way too complex for the task. I'm a fun of various one-liner solutions allowing to pack a lot more features in a small batch. In this case variable names aren't known to the function since it validates various purpose inputs sent to it via arguments with validation criteria.

For now, I just use 2 similar arguments with a function, i.e. "%opt%" and "opt" to use them both ways without extraction:
a) in FOR /F loop use `echo %~1` to validate %opt% value manually entered with Set /P, and b) if not passed validation, setting the default value "%~2=5" also sets "opt=5" without extracting the variable name, so the same variable can be used later in the batch. This seems to be the simplest way to accomplish the task, since extracting a substring like "opt" from an argument "%opt%" seems to need more code as not supported in Cmd. No "shortcut" suggestions coming so far. :)
Last edited by sambul35 on 27 May 2016 12:17, edited 2 times in total.

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Extracting substring from a batch or function argument

#6 Post by thefeduke » 27 May 2016 02:39

sambul35 wrote:extracting a substring like "opt" from an argument "%opt%" seems to need more code as not supported in Cmd.
You have already done what you want if you can work with %~2. If you add

Code: Select all

Echo.%Var2% %~2
to the code in your first post and pass to it the two parameters described in you most recent post, the contents of both should be the same. Whatever you did with %~2 that satisfied you, can be done with %var2%.

John A.

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Extracting substring from a batch or function argument

#7 Post by sambul35 » 27 May 2016 06:50

thefeduke wrote:Whatever you did with %~2 that satisfied you, can be done with %var2%.

Not really. I want to keep using the same variable name "opt" throughout the batch - before and after calling any function. If substituted with "var2", I'd have to use "var2" in the batch after calling the function, and can't assign its value to "opt", as "opt" name remains unknown unless found with FIND or FOR extra code (it can be any batch variable sent as a function argument).

Run this code to see the difference, i.e. when "var2" changes "opt" value doesn't, but when %~2 changes "opt" value changes too:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set "opt=t"
call :fun0 "%opt%" ""
echo. & echo Result: %opt% !opt!
call :fun1 "%opt%" "opt"
echo. & echo Result: %opt% !opt! & pause

:end
exit /b

:fun0
echo. & echo One argument: & echo Originals: %~1 %opt%
set "var1=%1" & set "var2=!var1:~2,3!"
echo Substitutions: !var1! '!var2!'
set "var2=5" & echo Changing var2 doesn't change "opt": !var2! %opt%
(goto) 2>nul
exit /b

:fun1
echo. & echo Two arguments: & echo Originals: %~2 %opt%
set "%~2=5"
echo Changing %%2 changes "opt": %~2 %opt%
(goto) 2>nul
exit /b

Can someone suggest a simple code, causing "opt" value change synchronously with "var2"? Or to find out "opt" variable name sent as an argument, i.e extract a substring from a batch "variable" argument without changing the variable name? :P

Relevant question: is there a way to suppress variable expansion, like %opt% ?
I found an interesting way to print content of a batch argument, but it doesn't seem helpful for the task:

Code: Select all

rem This will print a defined (opt) variable name entered as argument
echo %~1% %opt%
Last edited by sambul35 on 29 May 2016 07:50, edited 1 time in total.

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Extracting substring from a batch or function argument

#8 Post by thefeduke » 28 May 2016 21:22

sambul35 wrote:I want to keep using the same variable name "opt" throughout the batch - before and after calling any function.
First of all, thank you for the illustrative code. I, for one, find that this the first time in this topic to have a chance at understanding what you wanted. Now, perhaps the better minds can offer you more than pointers to sources for the rules.

Since you do not seem to be accepting values for OPT in the call, there must be an action plan if the string "%opt%" as the first argument. You do have an awareness of the name OPT because you want to use that name and want to set it to a value if that string is inputted. After calling :fun0 and :fun1 you have used that name to check the value.

If the variable OPT is undefined, the quoted string is passed from the command line. If OPT has a value of ANYTHING, then the passed value is "ANYTHING" not the string "%OPT%". These rules do not apply within a batch script, because substitution always takes place and "%opt%" will change to "" after substituting an undefined value. The function :fun0 cannot get at the value OPT, only at the value of OPT, as could be seen with ECHO ON before the CALL.

Any attempt to Set a substring will operate on the value 't' in your example, not the desired string. Incidentally the statement - set "var2"="%var1:~2,3%" - is not setting a value for var2 but for var2". The variable name is everything before the = symbol and var2 remains undefined. I also note that you access %opt% in the last ECHO in both :fun0 and :fun1. This works fine for :fun1, but :fun0 is doomed to return the previous value of 'opt' unless !opt! is used because the ECHO is compounded to the SET statement with '&', but stands alone in :fun1.

The previous comments were recovered from a draft created on Friday. I have done some experimentation in the interim.

I have modified your illustrative code:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set "opt=t"
@echo on
call :fun0 %%%1%% ""
echo. & echo Result: %opt% !opt!
Set AnyVar
Set %AnyVar%
echo. & echo Result2: %opt2% !opt2!
@echo on
call :fun1 "%opt%" "opt"
echo. & echo Result: %opt% !opt!
echo. & echo Result2: %opt2% !opt2!
pause

:end
exit /b

:fun0
@echo off
echo. & echo One argument: & echo Originals: %~1 %opt%
set "AnyVar=%1"
echo Substituting: '!AnyVar!'
Set %AnyVar%=8 & echo Changing AnyVar does so change "%AnyVar%": !AnyVar! !opt!
exit /b

:fun1
@echo off
echo. & echo Two arguments: & echo Originals: %~2 %opt%
set "%~2=5"
echo Changing %%2 changes "opt": %~2 %opt%
(goto) 2>nul
exit /b

The magic expression is %%%~1%%. This causes "%opt%" to become %"%opt%"% when received by :fun0 and the onion is peeled by variable substitution. The %"% at the beginning and end become null because the environmental variable " is not defined and all that is left is opt, the desired result.

This expression allows a string other that opt and of unknown length to be introduced by an argument and that name will be set to a value and can be examined using AnyVar and manipulated using AnyVar or its own name.

John A.

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Extracting substring from a batch or function argument

#9 Post by sambul35 » 29 May 2016 08:29

Thanks John!

I corrected typos in my above snippet following your notes. Its not clear what "opt2" means in your code, while its value remains empty? As well, your example output doesn't seem to show the claimed result: Changing AnyVar does change %AnyVar% ?

Still I'm afraid there's some misunderstanding here. :wink: I do know variable names, but the batch code below doesn't. As I mentioned, the function is used for verification of entered values of various arguments throughout the batch. Let's test another snippet as an example:

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set "mes1=Invalid value" & set "mes2=Set default value" & echo/
set "ring1=(New-Object Media.SoundPlayer "C:\Windows\Media\ringout.wav").PlaySync();"

:inp1
set /p "opt=Enter "opt" value > "
echo/ & call :fun0 "v" "klostu" "%mes1%" "%mes2%" "t" "!opt!" "opt" ":inp1"
echo opt=%opt% & echo/

:inp2
set /p "war=Enter "war" value > "
echo/ & call :fun0 "v" "0123456789" "%mes1%" "%mes2%" "5" "!war!" "war" ":inp2"
echo war=%war% & echo/

:end
pause
endlocal
exit /b

:fun0
rem Verify typed variable values
set "lnk="
if "%~1"=="v" (set "var="
   for /f "usebackq delims=%~2" %%k in (`echo %~6`) do (set "var=%%k")
   if defined var (
      set "%~7=" & echo %~3 & echo/
      powershell -c "%ring1%"
      choice /c yn /m "%~4 %~5 ?" /t 10 /d y
      echo/ & if !errorlevel! equ 1 (set "%~7=%~5") else (set "%~7=" & set "lnk=%~8"))
) else if "%~1"=="k" (
rem Add more verification types here...
)
(goto) 2>nul & if not "!lnk!"=="" (goto !lnk!)
exit /b


Could you illustrate, how would your suggested solution fit into this, if I omit 7th (i.e. duplicate) argument in the Call statement? Or, may be a different solution would work?
Last edited by sambul35 on 29 May 2016 17:04, edited 7 times in total.

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Extracting substring from a batch or function argument

#10 Post by thefeduke » 29 May 2016 11:19

Sorry, I did not mention that you could try running my script using the other anticipated argument "%opt2%" and perhaps an unexpected one like "%SomeThingElse%" instead of "%opt%" to observe the results. Meanwhile, I get back about your new snippet later

John A.

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Extracting substring from a batch or function argument

#11 Post by thefeduke » 30 May 2016 16:14

See what this does for you, sambul35. It is based on your seventh snippet version. Try it with "%opt%", "%war%", "%odd%", etc. I see that you are now really using both %6 and &7.

Code: Select all

@echo off
::http://www.dostips.com/forum/viewtopic.php?p=46771#p46771
:: Post subject: Re: Extracting substring from a batch or function argument
::Modified: May 30, 2016 by TheFeDuke
setlocal EnableDelayedExpansion
Call set InputVar=%%%1%%
Call set %InputVar%=s
echo/
Call Echo.Input argument has set variable name '%InputVar%' to value '%%%InputVar%%%'
echo/
set "mes1=Invalid value" & set "mes2=Set default value" & echo/
set "ring1=(New-Object Media.SoundPlayer "C:\Windows\Media\ringout.wav").PlaySync();"

:inp1
echo on
Call set /p "%InputVar%=Enter "%InputVar%" value > "
call :fun0 "v" "klostu" "%mes1%" "%mes2%" "t" "%%%InputVar%%%" "%InputVar%" ":inp1"
@echo off
If /I "%InputVar%" NEQ "opt" (
    call echo %InputVar%=%%%InputVar%%% but opt was not used.
) Else echo opt=%opt%

:inp2
set /p "war=Enter "war" value > "
echo/
echo on
call :fun0 "v" "0123456789" "%mes1%" "%mes2%" "5" "!war!" "war" ":inp2"
@echo off
echo war=%war% & echo/

:end
pause
endlocal
exit /b

:fun0
@echo off
rem Verify typed variable values
set "lnk="
if "%~1"=="v" (set "var="
   for /f "usebackq delims=%~2" %%k in (`echo %~6`) do (set "var=%%k")
   if defined var (
      set "%~7=" & echo %~3 %~6 & echo/
      powershell -c "%ring1%"
      choice /c yn /m "%~4 %~5 ?" /t 10 /d y
      echo/ & if !errorlevel! equ 1 (set "%~7=%~5") else (set "%~7=" & set "lnk=%~8"))
) else if "%~1"=="k" (
rem Add more verification types here...
)
(goto) 2>nul & if not "!lnk!"=="" (goto !lnk!)
exit /b

John A.

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Extracting substring from a batch or function argument

#12 Post by sambul35 » 30 May 2016 19:34

Hi Jonh,

It prints a real mess in Win10. I'm curios if you tested the above? :D Also, even if we assume it supposed to work, there seems to be no benefit in argument count.

As well, I was unable to get any positive result like "Changing AnyVar does change %AnyVar%" from your previous snippet. May be you can post the Cmd output that confirms your findings by values (not words) in your Windows version for both your snippets?

Anyway, thanks for trying, you were the only one to report any relevant tests here. However, the above task is not a trickery exercise. What I try to accomplish is reusing verification code as much as possible with various variables, and for that I need more flexibility in argument sequencing, and to limit the argument count to minimum. I usually get bored by what some call a "pro code", meaning each section is plain vanilla anyone can figure in a sec, which often results in tons of repeated code slowing system to near death condition. :wink:
Last edited by sambul35 on 31 May 2016 06:40, edited 1 time in total.

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Extracting substring from a batch or function argument

#13 Post by thefeduke » 31 May 2016 00:30

Here is one real mess on my system. I left some Echo ON statements in to watch the call that do not help the look.

Code: Select all

C:\Users\Jani\Scripts>pctsmbl2 "%war%"

Input argument has set variable name 'war' to value 's'



C:\Users\Jani\Scripts>Call set /p "war=Enter "war" value > "
Enter "war" value > x

C:\Users\Jani\Scripts>call :fun0 "v" "klostu" "Invalid value" "Set default value" "t" "%war%" "war" ":inp1"
Invalid value x

Set default value t ? [Y,N]?N


C:\Users\Jani\Scripts>Call set /p "war=Enter "war" value > "
Enter "war" value > 7

C:\Users\Jani\Scripts>call :fun0 "v" "klostu" "Invalid value" "Set default value" "t" "%war%" "war" ":inp1"
Invalid value 7

Set default value t ? [Y,N]?Y

war=t but opt was not used.
Enter "war" value > k


C:\Users\Jani\Scripts>call :fun0 "v" "0123456789" "Invalid value" "Set default value" "5" "!war!" "war" ":inp2"
Invalid value k

Set default value 5 ? [Y,N]?Y

war=5

Press any key to continue . . .
That is snippet two on 32-bit Windows 10 and it is the same on 64-bit Windows 7. I did not alter :fun1 but the test example did pass the WAR variable through :fun0 to :fun1 without actually coding its name until :Fun1.

As for economy of argument count, that is all your doing in the model supplied. :fun0 in snippet one needed only one argument. Certainly both arguments six and seven are not required. Why pass the value when it can be determined from the variable name, but it was your snippet structure.

I was just offering proof of concept that one can operate with the input format "%opt%" without my understanding why anyone would want to get information in that format. I hope that helped and that you find the better way.

John A.

sambul35
Posts: 192
Joined: 18 Jan 2012 10:13

Re: Extracting substring from a batch or function argument

#14 Post by sambul35 » 31 May 2016 07:32

thefeduke wrote:Certainly both arguments six and seven are not required.

Of course, some args can be omitted at the expense of a more complex validation code that includes args verification and substitution. It may improve :fun0 flexibility though due to shorter args count, thus allowing for more variables to be checked by adding more args if needed without using Shift required for 10+ args. :)

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set "mes1=Invalid value" & set "mes2=Set default value" & echo/

:inp1
set /p "opt=Enter "opt" value > "
echo/ & call :fun0 "v" "klostu" "t"

:inp2
set /p "war=Enter "war" value > "
echo/ & call :fun0 "v" "0123456789" "5"

:inp3
rem Set more variables...

:end
echo opt=!opt! & echo/
echo war=!war! & echo/
pause
endlocal
exit /b

:fun0
rem Verify typed variable values
set "lnk=" & set "ver=0"
if "%~1"=="v" (set "var="
   if "%~3"=="t" ( set "tst=!opt!" & set "link=:inp1"
   ) else if "%~3"=="5" ( set "tst=!war!" & set "link=:inp2" & rem More args here...
   ) else if "%~3"=="x" ( rem Check more default values here to identify the verified variable...
   )
   for /f "usebackq delims=%~2" %%k in (`echo !tst!`) do (set "var=%%k")
   if defined var (set "ver=1")
) else if "%~1"=="k" ( rem Add another verification method here...
)
if !ver! equ 1 ( powershell -c echo `a
   echo %mes1% & echo/
   choice /c yn /m "%mes2% %~3 ?" /t 10 /d y
   echo/ & if !errorlevel! equ 1 (set "tst=%~3") else (set "tst=" & set "lnk=!link!")
   if "%~3"=="t" ( set "opt=!tst!"
   ) else if "%~3"=="5" ( set "war=!tst!"
   ) else if "%~3"=="x" ( rem Check more default values here to identify the verified variable...
   ))
(goto) 2>nul & if not "!lnk!"=="" (goto !lnk!)
exit /b
Last edited by sambul35 on 02 Jun 2016 16:43, edited 3 times in total.

thefeduke
Posts: 211
Joined: 05 Apr 2015 13:06
Location: MA South Shore, USA

Re: Extracting substring from a batch or function argument

#15 Post by thefeduke » 31 May 2016 15:25

sambul35 wrote:It prints a real mess in Win10. I'm curios if you tested the above? :D Also, even if we assume it supposed to work, there seems to be no benefit in argument count.

As well, I was unable to get any positive result like "Changing AnyVar does change %AnyVar%" from your previous snippet. May be you can post the Cmd output that confirms your findings by values (not words) in your Windows version for both your snippets?
Well I guess that I still owe you snippet one findings:

Code: Select all

C:\Users\Zani\Scripts>pctsmbl1 "%opt%"

C:\Users\Zani\Scripts>call :fun0 %"%opt%"% ""

One argument:
Originals: opt t
Substituting: 'opt'
Changing AnyVar does so change "opt": opt 8

Result: 8  8
AnyVar=opt
opt=8

Result2:

C:\Users\Zani\Scripts>call :fun1 "8 " "opt"

Two arguments:
Originals: opt 8
Changing %2 changes "opt": opt 5

Result: 5 5

Result2:
Press any key to continue . . .
Now, I am curious as to how you did not get the same result. Perhaps you could show how you invoked my Snippet one version to get the different output that you describe and also do not show. We should verify common ground.
John A.

Post Reply