Включить/отключить отложенное расширение в цикле FOR /f (пакетном)

Я все еще пытаюсь правильно понять поведение Disable/EnableDelayedExpansion...

Я хочу проанализировать входные аргументы при вызове чего-то вроде command -a -b -c file, чтобы наконец получить options=-a -b -c и filename=file.

Для этого я использую цикл FOR /f:

set "count=0"
set "opts="
set "fl="
set tmpv=

:argloop
for /f tokens^=1^,^*^ delims^= %%a in ("%1") do (

    echo.
    echo Chosen option is %1
    set /a count+=1
    echo.
    echo Reading %count% is %%a..

    set "tmpv=%%a"
    rem setlocal enabledelayedexpansion
    echo Tmp is %tmpv% after set equal %%variable.
    rem endlocal
    rem setlocal disabledelayedexpansion
    set "tmpv=%tmpv:-=%"
    rem setlocal enabledelayedexpansion
    echo After removing it writes !tmpv!
    rem endlocal
    rem setlocal disabledelayedexpansion

    if "%tmpv%"=="%%a" (
        echo Input does not contain "-"
        set "fl=%tmpv%"
        echo %fl%

    ) else (

        echo/Options before are %opts%
        echo.
        if "%opts%"=="" (
            echo Options are empty.
            set opts=%%a
        ) else (
            set "opts=%opts% %%a"
        )
    )
    if not "%2"=="" (shift & goto:argloop)
)

echo.
echo Finally options are %opts%
set opts=%opts:-=/%
echo Finally options are %opts%
echo File name %fl%
set tmpv=
set count=
goto:end

Вывод пишет:

Chosen option is -a

Reading 1 is -a..
Tmp is  after set equal %variable.
After removing it writes
Options before are

Options are empty.

Chosen option is -b

Reading 2 is -b..
Tmp is -= after set equal %variable.
After removing it writes -=
Options before are -a


Chosen option is -c

Reading 3 is -c..
Tmp is = after set equal %variable.
After removing it writes =
Options before are -a -b


Chosen option is flfl

Reading 4 is flfl..
Tmp is = after set equal %variable.
After removing it writes =
Options before are -a -b -c


Finally options are -a -b -c flfl
Finally options are /a /b /c flfl
File name

Я заставил его работать с EnableDelayedExpansion, но не смог сохранить окончательную переменную %fl%.

Но почему так не работает (без использования отложенных расширений)??

Буду искренне признателен тем, кто постарается разъяснить это во всей полноте.


person mEm    schedule 27.12.2020    source источник


Ответы (1)


Правила на самом деле не слишком сложны.

Вы знаете, что %var% преобразуется в значение var.

Когда цикл анализируется, каждое %var% в этом цикле заменяется ТОГДА текущим значением var. Сюда входят такие псевдопеременные, как %cd%, %errorlevel% и %random%.

Если действует delayedexpansion (и действует с инструкции setlocal enabledelayedexpansion [start-of-setlocal-bracket] до достижения endlocal или end-of-file [end-of-setlocal-bracket]), то !var! преобразуется в содержимое var во время выполнения этой конкретной инструкции (set, echo и т. д.).

Если delayedexpansion НЕ действует, то !var! это просто буквальная строка !var!.

И один небольшой косяк. Любое изменение, внесенное в среду (добавление, удаление или изменение значений переменных), отбрасывается, когда заканчивается скобка setlocal.

Так что, по всей вероятности, вы могли бы отобразить разницу, echoпоставив %var% рядом с !var!.

  • а %%x (metavariable) всегда преобразуется в свое текущее значение, независимо от статуса setlocal.

[После ответов]

Поскольку в опубликованном коде все скобки setlocal enabledelayedexpansion/endlocal remmed-out, я не удивлен результатами.

Однако запуск опубликованного кода не дает опубликованных результатов - для меня ответ был Reading 0... Reading 3.

Итак, глядя на цикл for, я считаю, что он эквивалентен

for /f "tokens=1,* delims=" %%a in ("%1") do (

что, в свою очередь, совпадает с

for /f "delims=" %%a in ("%1") do (

поскольку разделителей нет, это фактически то же самое, что и

for %%a in (%1) do (

который ничего не делает, кроме присваивания %1 %%a и превращения всего цикла в один block statement.

Таким образом, этот код должен выполнять ту же работу - без извращений, предоставляемых setlocal...

@ECHO OFF
SETLOCAL

set "count=0"
set "opts="
set "fl="
set "tmpv="

:argloopn
SET "arg=%1"
IF NOT DEFINED arg GOTO report
SET /a count+=1
ECHO arg %count% is %1 IN variable ARG = "%arg%"
SET "tmpv=%arg%"
rem remove "-"
set "tmpv=%tmpv:-=%"
IF "%tmpv%"=="%arg%" (
    echo Input does not contain "-"
    set "fl=%tmpv%"
) ELSE (
    ECHO Input contains "-" so is option
    SET "opts=%opts% %arg%"
)
SHIFT
GOTO argloopn

:report

rem fix-up options since 1st char, if it exists must be a space as [space]newoption is added each time
IF DEFINED options SET "options=%options:~1%"
echo Finally options are %opts%
set opts=%opts:-=/%
echo Finally options are %opts%
echo File name %fl%


echo.
GOTO :EOF

Я предположил, что создана надлежащая среда пакетной отладки; следовательно, goto :eof для завершения пакета и начальный setlocal для сохранения исходной среды.

Когда вы используете метод «укажи и хихикай» для выполнения пакета, окно пакета часто закрывается, если обнаруживается синтаксическая ошибка. Вместо этого вам следует открыть "командную строку" и запустить пакет оттуда, чтобы окно оставалось открытым. и любое сообщение об ошибке будет отображаться.

--- НО ---

При тестировании я пробовал это:

@ECHO OFF
:parasite
setlocal
set "opts="
:loopp
FOR /f "delims=" %%a IN ("%1") DO (
 SET "opts=%opts% %%a"
 SETLOCAL ENABLEDELAYEDEXPANSION
  ECHO OPTS was "%opts%" is now "!opts!"
 ENDLOCAL
 ECHO %%opts%% is "%opts%" and !opts! is "!opts!"
)
SHIFT
IF "%1"=="" GOTO :EOF 
GOTO loopp

Что не сделало того, что я ожидал, то есть

 %opts% is " -a -b -c file" and !opts! is "!opts!"

Вместо этого сообщалось

 %opts% is " -a -b -c file" and  -a -b -c file is " -a -b -c file"

Что меня озадачивает, так как !var! находится вне командной скобки setlocal enabledelayedexpansion/endlocal и, следовательно, не должно быть заменено, на мой взгляд.

Мне кажется, проблема с @jeb... так что я посмотрю, есть ли у него объяснение...

person Magoo    schedule 28.12.2020
comment
Я вижу, но именно потому, что это не должно быть так сложно, я действительно не понимаю, что там происходит. И %tmpv%, и !tmpv! ведут себя не так, как предполагалось.. ты это видишь? Я имею в виду строки echo Tmp is %tmpv% after set equal %%variable и echo After removing it writes !tmpv!. В этом последнем очевидно, как !tmpv! не читается буквально. Наконец, поскольку я не использую никаких setlocal enabledelayedexpansion, все должно обрабатываться с помощью %tmpv%, не так ли? - person mEm; 28.12.2020
comment
Проблема в том, что вы меняете переменные в блоке кода (ваш цикл for), поэтому вам нужно отложенное расширение, чтобы использовать эти измененные переменные. Прочтите это (при необходимости несколько раз; это ключ к пониманию вашей проблемы) - person Stephan; 28.12.2020
comment
@jeb: Не хочешь прокомментировать мое последнее наблюдение здесь? Возможно, слишком много рождественского такера? - person Magoo; 28.12.2020