Recursively expand variables in strings
Posted: 13 Feb 2022 15:43
Hello everyone,
I wanted to share a function i just made to recursively expand variables in strings.
I've searched for it here and havn't come accross it on the forum so i thought to post it here.
It works for me for most and simple cases, i hope it may be of help to others.
Function to recursively expand variables in strings:
Test code:
Results:
Normal expansion: "start_string !test4! string_in_between !test2! some string end"
Delayed expansion: "start_string !test4! string_in_between !test2! some string end"
Results of recursive expansion displayed with %: "start_string a value 123 456 789 10 string_in_between a value 123 456 some string end"
Results of recursive expansion displayed with !: "start_string a value 123 456 789 10 string_in_between a value 123 456 some string end"
Edit: not thoroughly tested with strange characters though, but recursive expansion of simple variables works.
Update about special characters thus far:
Because of the recursion, special characters ^ ! % are resolved with each iteration. This means that literally meant special characters need as many escaping as the number of nestings and i can't figured how out to make this work at the moment.
Character ^ when needed literally, not to expand, can be used but is ultimately resvoled into nothing when nesting level of variables is deep.
Same applies to character %, it needs double escaping in strings when used literally: "%%%%" AND it can only be used BEFORE the first variable, like: set "test5=start_string %%%% !test4! string_in_between !test2! some string". It is because of the way the function currently works, all other percent signs are expanded.
Character ! cannot be used literally, even double escaping won't work and the function won't expand the variables in the string correctly.
Other special characters like `~&*[]()<>'" are no problem.
Above example adjusted with special characters %`~&*[]()<>'" but without exclamation mark ! special character:
Gives following results:
Normal expansion: "start_string %% ~`^^&[]()<>''"" !test4! string_in_between ~`^^&[]()<>''"" !test2! some string ~`^^&[]()<>''"" end"
Delayed expansion: "start_string %% ~`^^&[]()<>''"" !test4! string_in_between ~`^^&[]()<>''"" !test2! some string ~`^^&[]()<>''"" end"
Results of recursive expansion displayed with %: "start_string % ~`^&[]()<>''"" a value 123 ~`^^&[]()<>''"" 456 ~`^&[]()<>''"" 789 ~`^&[]()<>''"" 10 ~`&[]()<>''"" string_in_between ~`&[]()<>''"" a value 123 ~`^&[]()<>''"" 456 ~`&[]()<>''"" some string ~`&[]()<>''"" end"
Results of recursive expansion displayed with !: "start_string % ~`^&[]()<>''"" a value 123 ~`^^&[]()<>''"" 456 ~`^&[]()<>''"" 789 ~`^&[]()<>''"" 10 ~`&[]()<>''"" string_in_between ~`&[]()<>''"" a value 123 ~`^&[]()<>''"" 456 ~`&[]()<>''"" some string ~`&[]()<>''"" end"
Currently looking for way to have the function preserve literally meant special characters ^ % ! with each recursive call of the function. Using 'CALL set "Expand.Var=!Expand.Var!%%~b"' or 'CALL set "%~2=%%~c"' in the function does not solve the issue, neither does 'set "Expand.Var=!Expand.Var:%%=%%%%!"' solve it for percent signs.
Suggestions are welcome.
Edit: some typos.
I wanted to share a function i just made to recursively expand variables in strings.
I've searched for it here and havn't come accross it on the forum so i thought to post it here.
It works for me for most and simple cases, i hope it may be of help to others.
Function to recursively expand variables in strings:
Code: Select all
:Expand Text [Rtn]
:: @param (str) Text text with variables to expand
:: @param (str) Rtn return variable
:: @return (str) Text text with expanded variables
setlocal enableDelayedExpansion
for /f "tokens=1,* delims=!" %%b in ("%~1") do (
if "!%%~b!" neq "" (
call:Expand "!%%~b!%%~c" "Expand.Var"
) else (
set "Expand.Var=!Expand.Var!%%~b"
if "!%%~c!" neq "" call:Expand "!%%~c!" "Expand.Var"
)
)
for /f "usebackq tokens=1,* delims==" %%b in (
`set Expand.Var`) do (endlocal
if "%~2" neq "" if "%~2" neq " " set "%~2=%%~c"
)
exit /b
Code: Select all
@echo off
REM Code to test recursively expand variables in strings:
set "test1=a value 123"
set "test2=!test1! 456"
set "test3=!test2! 789"
set "test4=!test3! 10"
set "test5=start_string !test4! string_in_between !test2! some string"
echo(Normal expansion: "%test5% end"
setlocal enableDelayedExpansion
echo(Delayed expansion: "!test5! end"
endlocal
set MyRtn=
call:Expand "%test5% end" MyRtn
echo(Recursive expansion displayed with %%: "%MyRtn%"
set MyRtn=
setlocal enableDelayedExpansion
call:Expand "!test5! end" MyRtn
echo(Recursive expansion displayed with ^^!: "!MyRtn!"
endlocal
exit /b
:Expand Text [Rtn]
:: @param (str) Text text with variables to expand
:: @param (str) Rtn return variable
:: @return (str) Text text with expanded variables
setlocal enableDelayedExpansion
for /f "tokens=1,* delims=!" %%b in ("%~1") do (
if "!%%~b!" neq "" (
call:Expand "!%%~b!%%~c" "Expand.Var"
) else (
set "Expand.Var=!Expand.Var!%%~b"
if "!%%~c!" neq "" call:Expand "!%%~c!" "Expand.Var"
)
)
for /f "usebackq tokens=1,* delims==" %%b in (
`set Expand.Var`) do (endlocal
if "%~2" neq "" if "%~2" neq " " set "%~2=%%~c"
)
exit /b
Normal expansion: "start_string !test4! string_in_between !test2! some string end"
Delayed expansion: "start_string !test4! string_in_between !test2! some string end"
Results of recursive expansion displayed with %: "start_string a value 123 456 789 10 string_in_between a value 123 456 some string end"
Results of recursive expansion displayed with !: "start_string a value 123 456 789 10 string_in_between a value 123 456 some string end"
Edit: not thoroughly tested with strange characters though, but recursive expansion of simple variables works.
Update about special characters thus far:
Because of the recursion, special characters ^ ! % are resolved with each iteration. This means that literally meant special characters need as many escaping as the number of nestings and i can't figured how out to make this work at the moment.
Character ^ when needed literally, not to expand, can be used but is ultimately resvoled into nothing when nesting level of variables is deep.
Same applies to character %, it needs double escaping in strings when used literally: "%%%%" AND it can only be used BEFORE the first variable, like: set "test5=start_string %%%% !test4! string_in_between !test2! some string". It is because of the way the function currently works, all other percent signs are expanded.
Character ! cannot be used literally, even double escaping won't work and the function won't expand the variables in the string correctly.
Other special characters like `~&*[]()<>'" are no problem.
Above example adjusted with special characters %`~&*[]()<>'" but without exclamation mark ! special character:
Code: Select all
@echo off
REM Code to test recursively expand variables in strings:
set "test1=a value 123 ~`^^&[]()<>''"""
set "test2=!test1! 456 ~`^^&[]()<>''"""
set "test3=!test2! 789 ~`^^&[]()<>''"""
set "test4=!test3! 10 ~`^^&[]()<>''"""
set "test5=start_string %%%% ~`^^&[]()<>''"" !test4! string_in_between ~`^^&[]()<>''"" !test2! some string ~`^^&[]()<>''"""
echo(Normal expansion: "%test5% end"
setlocal enableDelayedExpansion
echo(Delayed expansion: "!test5! end"
endlocal
set MyRtn=
call:Expand "%test5% end" MyRtn
echo(Recursive expansion displayed with %%: "%MyRtn%"
set MyRtn=
setlocal enableDelayedExpansion
call:Expand "!test5! end" MyRtn
echo(Recursive expansion displayed with ^^!: "!MyRtn!"
endlocal
exit /b
:Expand Text [Rtn]
:: @param (str) Text text with variables to expand
:: @param (str) Rtn return variable
:: @return (str) Text text with expanded variables
setlocal enableDelayedExpansion
for /f "tokens=1,* delims=!" %%b in ("%~1") do (
if "!%%~b!" neq "" (
call:Expand "!%%~b!%%~c" "Expand.Var"
) else (
set "Expand.Var=!Expand.Var!%%~b"
if "!%%~c!" neq "" call:Expand "!%%~c!" "Expand.Var"
)
)
for /f "usebackq tokens=1,* delims==" %%b in (
`set Expand.Var`) do (endlocal
if "%~2" neq "" if "%~2" neq " " set "%~2=%%~c"
)
exit /b
Normal expansion: "start_string %% ~`^^&[]()<>''"" !test4! string_in_between ~`^^&[]()<>''"" !test2! some string ~`^^&[]()<>''"" end"
Delayed expansion: "start_string %% ~`^^&[]()<>''"" !test4! string_in_between ~`^^&[]()<>''"" !test2! some string ~`^^&[]()<>''"" end"
Results of recursive expansion displayed with %: "start_string % ~`^&[]()<>''"" a value 123 ~`^^&[]()<>''"" 456 ~`^&[]()<>''"" 789 ~`^&[]()<>''"" 10 ~`&[]()<>''"" string_in_between ~`&[]()<>''"" a value 123 ~`^&[]()<>''"" 456 ~`&[]()<>''"" some string ~`&[]()<>''"" end"
Results of recursive expansion displayed with !: "start_string % ~`^&[]()<>''"" a value 123 ~`^^&[]()<>''"" 456 ~`^&[]()<>''"" 789 ~`^&[]()<>''"" 10 ~`&[]()<>''"" string_in_between ~`&[]()<>''"" a value 123 ~`^&[]()<>''"" 456 ~`&[]()<>''"" some string ~`&[]()<>''"" end"
Currently looking for way to have the function preserve literally meant special characters ^ % ! with each recursive call of the function. Using 'CALL set "Expand.Var=!Expand.Var!%%~b"' or 'CALL set "%~2=%%~c"' in the function does not solve the issue, neither does 'set "Expand.Var=!Expand.Var:%%=%%%%!"' solve it for percent signs.
Suggestions are welcome.
Edit: some typos.