Batch - Set/Reset parameters calling subroutine wi

2019-05-14 11:32发布

问题:

I have 2 Locals using delayed expansion. The 1. Local calls a subroutine which includes the 2. Local which defines 2 variables and set the values of those as the parameter values using FOR /F .... This works fine as long as the string do not include one or more exclamation marks. The following working script shows the issue:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION

    CALL :get_substrings testString substr
    ECHO Inside 1. local:
    ECHO !testString!
    ECHO !substr!

    EXIT /B 0
ENDLOCAL


:get_substrings
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET string="World^! wasserschutzpolizei^!"
    SET substring="Hello^!"
    ECHO Inside get_substrings:
    ECHO !string!
    ECHO !substring!

    FOR /F "delims=" %%s IN ("!string!") DO FOR /F "delims=" %%b IN ("!substring!") DO (
        ENDLOCAL & SET "%1=%%s" & SET "%2=%%b" & EXIT /B 0
    )
EXIT /B 0

Output:

Inside get_substrings:
"World! wasserschutzpolizei!"
"Hello!"
Inside 1. local:
"World"
"Hello"

Expected:

Inside get_substrings:
"World! wasserschutzpolizei!"
"Hello!"
Inside 1. local:
"World! wasserschutzpolizei!"
"Hello!"

If you remove the exclamation marks in string and substring the script works as expected.

Because this is just an Example consider...

  1. Not using delayed expansion is not an option and also...
  2. Using one Local is undesirable.

Of course, if one or even both things are REALLY required and there is no other way I accept other answers pointing that out.

So...

  1. How can get the values with exclamation mark in the 1. local like i showed in the Expected part?
  2. Why doesn't it work with the current way of setting the param values?

Appreciate your help!

回答1:

Change the return part of your :get_substrings function to:

...
set "return1=!string:"=""!"
set "return1=%return1:!=^^^!%"
set "return1=!return1:""="!"

set "return2=!substring:"=""!"
set "return2=%return2:!=^^^!%"
set "return2=!return2:""="!"

FOR /F "delims=" %%s IN ("!return1!") DO FOR /F "delims=" %%b IN ("!return2!") DO (
    ENDLOCAL & SET "%1=%%s" & SET "%2=%%b" & EXIT /B 0
)

But why your solution fails?
The problem is that the set "%1=%%s" is executed in a delayed expansion context, as your complete batch uses delayed expansion.
But delayed expansion is the last expansion phase of the batch parser and it's after the parameter expansion of the %%s.
Therefore the parser tries to expand your exclamation marks, when here is only one it will be removed, when there are two like in "World! wasserschutzpolizei!" it expand the variable ! wasserschutzpolizei! to it's value (in your case it seems to be empty).

The solution simply put carets in front of any exclamation mark.
But you are also using quotes in your samples it makes more things more complicated.
To avoid problems with quotes and carets it's an easy way to double all quotes, add the carets and the change the doubled quotes back to single quotes.



回答2:

Is it an option for you to have delayed expansion disabled while calling the sub-routine:

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION

    CALL :get_substrings testString substr
    SETLOCAL ENABLEDELAYEDEXPANSION
    ECHO Inside 1. local:
    ECHO !testString!
    ECHO !substr!
    ENDLOCAL

    EXIT /B 0
ENDLOCAL


:get_substrings
    SETLOCAL ENABLEDELAYEDEXPANSION

    SET string="World^! wasserschutzpolizei^!"
    SET substring="Hello^!"
    ECHO Inside get_substrings:
    ECHO !string!
    ECHO !substring!

    FOR /F "delims=" %%s IN ("!string!") DO FOR /F "delims=" %%b IN ("!substring!") DO (
        ENDLOCAL & SET "%1=%%s" & SET "%2=%%b" & EXIT /B 0
    )
EXIT /B 0

This would avoid to expand the for variables %%s and %%b when delayed expansion is enabled, because this is the last step, so exclamation marks became consumed.