Сохранение восклицательных знаков в переменной между пакетами setlocals

Попытка сохранить восклицательные знаки в переменных. Упрощенный, иллюстративный скрипт ниже:

    echo off
    set testvar="C:\Windows\TestOfIllegals[!]"
    echo Pre-EnableDE: %testvar%

    setlocal enableextensions enabledelayedexpansion
    echo Post-EnableDE: %testvar%

    Setlocal DisableDelayedExpansion
    echo ssetlocal sub-instance...
    echo TestVar after re-disableDE: %testvar%
    set modTestVar=%testvar%
    echo TestVar to new var, modTestVar: %modTestVar%
    endlocal & set "RetVar2=%modTestVar%"

    echo modTestVar back in main script: %RetVar2%

    Setlocal DisableDelayedExpansion
    echo modTestVar, main script in another setlocal diasbleDE instance: %RetVar2%
    endlocal

    pause
    exit /b

это производит вывод:

    Pre-EnableDE: "C:\Windows\TestOfIllegals[!]"
    Post-EnableDE: "C:\Windows\TestOfIllegals[]"
    ssetlocal sub-instance...
    TestVar after re-disableDE: "C:\Windows\TestOfIllegals[!]"
    TestVar to new var, modTestVar: "C:\Windows\TestOfIllegals[!]"
    modTestVar back in main script: "C:\Windows\TestOfIllegals[]"
    modTestVar, main script in another setlocal diasbleDE instance: "C:\Windows\TestOfIllegals[]"

Почему восклицательный знак не сохраняется в modTestVar? Есть какой-либо способ сделать это?

(Я знаю, что люди говорили, прежде чем "опубликовать полный сценарий" - но довольно долго, и это представляет основную проблему. Тем не менее, рад опубликовать, если полезно).

Спасибо

1 ответ

Решение

Он сохраняется в переменной, но вам нужно использовать отложенное расширение.

Когда вы использовали процентное расширение в режиме отложенного расширения, переменная будет расширена, и содержимое, особенно восклицательные знаки, будет проанализировано и позже, и один восклицательный знак будет просто удален.

echo off
set testvar="C:\Windows\TestOfIllegals[!]"
echo Pre-EnableDE: %testvar%

setlocal enableextensions enabledelayedexpansion
echo Post-EnableDE: !testvar!

Setlocal DisableDelayedExpansion
echo ssetlocal sub-instance...
echo TestVar after re-disableDE: %testvar%
set modTestVar=%testvar%
echo TestVar to new var, modTestVar: %modTestVar%
endlocal & set "RetVar2=%modTestVar%"

echo modTestVar back in main script: !RetVar2!

Setlocal DisableDelayedExpansion
echo modTestVar, main script in another setlocal diasbleDE instance: %RetVar2%
endlocal

pause

Другая / единственная проблема - когда вы пытаетесь передать переменную через эндокальный барьер (например, endlocal & set "RetVar2=%modTestVar%").
Это не тривиально.

Это пакетный макрос для этого, используется следующим образом%endlocal% modTestVar

setlocal DisableDelayedExpansion
set LF=^


set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
%=   I use EDE for EnableDelayeExpansion and DDE for DisableDelayedExpansion =%
set ^"endlocal=for %%# in (1 2) do if %%#==2 (%\n%
   setlocal EnableDelayedExpansion%\n%
 %=       Take all variable names into the varName array       =%%\n%
   set varName_count=0%\n%
   for %%C in (!args!) do set "varName[!varName_count!]=%%~C" ^& set /a varName_count+=1%\n%
 %= Build one variable with a list of set statements for each variable delimited by newlines =%%\n%
 %= The lists looks like --> set result1=myContent\n"set result1=myContent1"\nset result2=content2\nset result2=content2\n     =%%\n%
 %= Each result exists two times, the first for the case returning to DDE, the second for EDE =%%\n%
 %= The correct line will be detected by the (missing) enclosing quotes  =%%\n%
   set "retContent=1!LF!"%\n%
   for /L %%n in (0 1 !varName_count!) do (%\n%
      for /F "delims=" %%C in ("!varName[%%n]!") DO (%\n%
         set "content=!%%C!"%\n%
         set "retContent=!retContent!"set !varName[%%n]!=!content!"!LF!"%\n%
         if defined content (%\n%
 %=      This complex block is only for replacing '!' with '^!'      =%%\n%
 %=    First replacing   '"'->'""q'   '^'->'^^' =%%\n%
         set ^"content_EDE=!content:"=""q!"%\n%
         set "content_EDE=!content_EDE:^=^^!"%\n%
 %= Now it's poosible to use CALL SET and replace '!'->'""e!' =%%\n%
         call set "content_EDE=%%content_EDE:^!=""e^!%%"%\n%
         %= Now it's possible to replace '""e' to '^', this is effectivly '!' -> '^!'  =%%\n%
         set "content_EDE=!content_EDE:""e=^!"%\n%
         %= Now restore the quotes  =%%\n%
         set ^"content_EDE=!content_EDE:""q="!"%\n%
         ) ELSE set "content_EDE="%\n%
         set "retContent=!retContent!set "!varName[%%n]!=!content_EDE!"!LF!"%\n%
      )%\n%
   )%\n%
 %= Now return all variables from retContent over the barrier =%%\n%
   for /F "delims=" %%V in ("!retContent!") DO (%\n%
 %= Only the first line can contain a single 1 =%%\n%
      if "%%V"=="1" (%\n%
 %= We need to call endlocal twice, as there is one more setlocal in the macro itself =%%\n%
         endlocal%\n%
         endlocal%\n%
      ) ELSE (%\n%
 %= This is true in EDE             =%%\n%
         if "!"=="" (%\n%
            if %%V==%%~V (%\n%
               %%V !%\n%
            )%\n%
         ) ELSE IF not %%V==%%~V (%\n%
            %%~V%\n%
         )%\n%
      )%\n%
   )%\n%
 ) else set args="
Другие вопросы по тегам