Синтаксическая ошибка в одном из двух почти одинаковых пакетных сценариев: ")" здесь не может быть синтаксически обработана - PullRequest
5 голосов
/ 12 марта 2020

Я пытаюсь настроить сервер Jenkins для автоматических c сборок Unity.

Поэтому я написал два (на мой взгляд) в основном идентичных пакетных сценария.

Оба сценария Дженкинс запускает шаги сборки как шаг Execute Windows batch command, используя

Команда: E:\unityImport.bat

и после этого второй шаг Execute Windows batch command с использованием

Команда: E:\unityBuild.bat

Они оба имеют одинаковое начало, так как мне нужно собрать несколько путей к файлам и, в частности, версию единства проекта. Поэтому в обоих сценариях я использую один и тот же способ синтаксического анализа и разбиения строк в версии проекта. Единственное, что отличается между ними, это то, что первый запускает Unity и импортирует выделенный unitypackage (который содержит метод для выполнения на следующем шаге) в проект, в то время как второй снова запускает Unity для выполнения фактической сборки ( к сожалению, это не сработало в одном go ... Кажется, Unity пытается выполнить метод до импорта unitypackage.

Однако второй скрипт всегда завершается ошибкой с синтаксической ошибкой

")" здесь невозможно синтаксически обработать.


Я пытаюсь достичь

  1. Считать содержимое файла %WORKSPACE%\ProjectSettings\ProjectVersion.txt

    SET /p TEST=<%WORKSPACE%\ProjectSettings\ProjectVersion.txt
    

    Содержимое %TEST% обычно выглядит, например,

    m_EditorVersion: 2019.3.4f1
    

    и ECHO. ProjectVersion.txt = %TEST% выглядит как

    ProjectVersion.txt = m_EditorVersion: 2019.3.4f1
    
  2. строка разбита, чтобы взять только последнюю часть, содержащую номер версии

    for %%x in (%TEST::= %) do (
        SET "VALUE=%%x"
        SET "UNITY_VERSION=!VALUE:~0,-2!" 
    )
    

    , поэтому %UNITY_VERSION% обычно содержит, например, 2019.3.4. Я не делю больше, потому что есть также версия Unity с двумя цифрами, например, 2018.4.18

  3. строка разделена на ., чтобы получить только основной номер релиза

    for /f "tokens=1,2 delims=." %%a in ("%UNITY_VERSION%") do (
        SET "A=%%a"
        SET "B=%%b"
    )
    SET "UNITY_VERSION=%A%.%B%"
    

    , что приводит к %UNITY_VERSION%, например, 2019.3

  4. Наконец, поиск во всех установленных версиях Unity, если требуемая версия присутствует

    set "UNITY_FOLDER="
    for /f "delims=" %%a in ('dir /b E:\Unity\%UNITY_VERSION%*') do (
        set "UNITY_FOLDER=%%a"
    )
    

    после этого мы либо нашли допустимую папку установки Unity для данной версии, либо нет.


Итак, вот сценарии.

Импорт (работает как положено)

@ECHO OFF
CLS
ECHO.

cd %WORKSPACE%

IF NOT EXIST %WORKSPACE%\ProjectSettings\ProjectVersion.txt (
    EXIT 1
)

SETLOCAL ENABLEDELAYEDEXPANSION

SET /p TEST=<%WORKSPACE%\ProjectSettings\ProjectVersion.txt
ECHO. ProjectVersion.txt = %TEST%

for %%x in (%TEST::= %) do (
    SET "VALUE=%%x"
    SET "UNITY_VERSION=!VALUE:~0,-2!" 
)

for /f "tokens=1,2 delims=." %%a in ("%UNITY_VERSION%") do (
    SET "A=%%a"
    SET "B=%%b"
)

SET "UNITY_VERSION=%A%.%B%"
ECHO. Project Unity Version = %UNITY_VERSION%

set "UNITY_FOLDER="
for /f "delims=" %%a in ('dir /b E:\Unity\%UNITY_VERSION%*') do (
    set "UNITY_FOLDER=%%a"
)

IF "%UNITY_FOLDER%"=="" (
    EXIT 1
)

ECHO. Using Unity Version %UNITY_FOLDER%

ECHO. Running:
ECHO. E:\Unity\%UNITY_FOLDER%\Editor\Unity.exe -quit -batchmode -projectPath %WORKSPACE% -logFile - -importPackage E:\UnityBuildPackage\AutoBuilder.unitypackage

E:\Unity\%UNITY_FOLDER%\Editor\Unity.exe -quit -batchmode -projectPath %WORKSPACE% -logFile - -importPackage E:\UnityBuildPackage\AutoBuilder.unitypackage

IF NOT %errorlevel% equ 0 (
    EXIT 1
) 

EXIT 0

Сборка (ошибка не возникает из-за синтаксической ошибки, которую я отмечу REM HERE IT BREAKS! ..., которой нет в реальном скрипте)

@ECHO OFF
CLS
ECHO.

cd %WORKSPACE%

IF NOT EXIST %WORKSPACE%\ProjectSettings\ProjectVersion.txt (
    EXIT 1
)

SETLOCAL ENABLEDELAYEDEXPANSION

SET /p TEST=<%WORKSPACE%\ProjectSettings\ProjectVersion.txt
ECHO. ProjectVersion.txt = %TEST%

REM HERE IT BREAKS! The before echo is the last I see before getting the syntax error

for %%x in (%TEST::= %) do (
    SET "VALUE=%%x"
    SET "UNITY_VERSION=!VALUE:~0,-2!" 
)

for /f "tokens=1,2 delims=." %%a in ("%UNITY_VERSION%") do (
    SET "A=%%a"
    SET "B=%%b"
)

SET "UNITY_VERSION=%A%.%B%"
ECHO. Project Unity Version = %UNITY_VERSION%

set "UNITY_FOLDER="
for /f "delims=" %%a in ('dir /b E:\Unity\%UNITY_VERSION%*') do (
    set "UNITY_FOLDER=%%a"
)

IF "%UNITY_FOLDER%"=="" (
    EXIT 1
)

ECHO. Using Unity Version %UNITY_FOLDER%

...

Не думаю, что все остальное имеет значение, поскольку, как сказано, то, что я вижу в консоли, уже ломается, например,

ProjectVersion.txt = 2019.3.4f1

")" здесь невозможно синтаксически обработать.


Кто-нибудь видит ошибку или может быть, с Дженкинсом есть что-то, что приводит к сбою второго скрипта с синтаксической ошибкой, хотя, насколько я вижу, они в основном идентичны?

1 Ответ

7 голосов
/ 14 марта 2020

Есть несколько небольших проблем с кодом, которые я объясняю один за другим ниже, мое предложение для пакетного файла.

Задача получить UNITY_FOLDER в соответствии с UNITY_VERSION, как определено в файле ProjectVersion.txt можно сделать более эффективным, используя следующий код:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

if not defined WORKSPACE (
    echo ERROR: Environment variable WORKSPACE is not defined.
    exit /B 1
)

if not exist "%WORKSPACE%\ProjectSettings\ProjectVersion.txt" (
    echo ERROR: File "%WORKSPACE%\ProjectSettings\ProjectVersion.txt" does not exist.
    exit /B 1
)

set "UNITY_FOLDER="
set "UNITY_VERSION="
for /F "usebackq tokens=2-4 delims=. " %%I in ("%WORKSPACE%\ProjectSettings\ProjectVersion.txt") do (
    if not "%%~K" == "" (
        for /F "delims=abcdef" %%L in ("%%~K") do (
            set "UNITY_VERSION=%%~I.%%~J.%%~L"
            for /D %%M in ("E:\Unity\%%~I.%%~J*") do set "UNITY_FOLDER=%%M"
        )
    )
)

if not defined UNITY_VERSION (
    echo ERROR: Failed to determine unity version from "%WORKSPACE%\ProjectSettings\ProjectVersion.txt".
    exit /B 1
)
if not defined UNITY_FOLDER (
    echo ERROR: Failed to find a folder in "E:\Unity" for unity version %UNITY_VERSION%.
    exit /B 1
)

echo Found for unity version %UNITY_VERSION% the folder "%UNITY_FOLDER%".

cd /D "%WORKSPACE%" 2>nul
if errorlevel 1 (
    echo ERROR: Failed to set "%WORKSPACE%" as current folder.
    exit /B
)
rem Other commands to execute.

endlocal

Этот пакетный файл сначала устанавливает среду выполнения, требуемую для этого пакетного файла, с помощью команды SETLOCAL .

Существование переменной среды WORKSPACE проверяется следующим пакетным файлом. Эта переменная среды должна быть определена Jenkins вне этого пакетного файла. При отсутствии определения этой важной переменной среды выводится сообщение об ошибке.

Затем проверяется наличие текстового файла, выводится сообщение об ошибке, если оно не существует, и выполняется выход из пакетного файла с кодом выхода 1.

Две переменные среды UNITY_FOLDER и UNITY_VERSION удаляются, если они определены случайно вне пакетного файла.

Затем обрабатывается текстовый файл, который должен содержать только одну непустую строку с интересующими данными , В противном случае было бы необходимо изменить код для оценки первой подстроки, если она равна m_EditorVersion: перед выполнением других команд.

FOR с параметром /F интерпретирует вложенный набор в " по умолчанию в качестве строки для обработки. Но в этом случае строка в " должна интерпретироваться как полное имя файла, содержимое которого должно обрабатываться построчно FOR . По этой причине опция usebackq используется для получения требуемого поведения при обработке содержимого файла.

FOR игнорирует всегда пустые строки при обработке содержимого файла. Поэтому не имеет значения, содержит ли текстовый файл вверху одну или несколько пустых строк.

FOR по умолчанию разбивает строку на подстроки, используя обычный пробел и символ горизонтальной табуляции в качестве разделителей строк. Если первая строка с пробелом / табуляцией начинается с точки с запятой, являющейся символом конца строки по умолчанию после удаления всех начальных пробелов / табуляций, строка также будет игнорироваться FOR , как пустая строка. Наконец, только первая строка с пробелом / табуляцией будет присвоена указанной переменной l oop I.

Это поведение по умолчанию при обработке строки здесь не требуется, так как назначается только m_EditorVersion:, назначенный указанному l oop переменная I недостаточна. По этой причине опция delims=. используется для разделения линии на точки и пробелы. Опция tokens=2-4 сообщает FOR , что второй подстроке с пробелами / точками с разделителями 2019 следует присвоить l oop переменную I, третьей подстроке с пробелами / точками с разделителями 3 следующую л oop переменная J, которая является следующим символом в таблице ASCII и четвертой подстрокой с пробелами / точками с разделителями 4f1 до следующей, кроме одной переменной l oop K.

Здесь важно указать delims=. в конце строки аргумента параметров с символом пробела в качестве последнего символа, поскольку символ пробела в противном случае интерпретируется как символ разделения параметров, который игнорируется, как пробел между usebackq и tokens=2-4 и расстояние между tokens=2-4 и delims=. . На самом деле можно также написать параметры без пробелов, например "usebackqtokens=2-4delims=. ", но это затрудняет чтение строки аргумента с параметрами.

Здесь можно сохранить определение конца строки по умолчанию eol=; поскольку строка с версией единицы в ProjectVersion.txt не имеет точки с запятой после 0 или более пробелов / точек и по этой причине никогда не игнорируется.

FOR запускает команды в блок команд, когда в строке была найдена, по крайней мере, вторая строка, разделенная пробелом / точкой, назначенная переменной l oop I, т. е. непустой строке назначена указанная переменная l oop I. Но команды должны выполняться только в том случае, если все три части единой версии определены с помощью FOR и назначены переменным l oop I, J и K. Поэтому выполняется простое сравнение строк, чтобы убедиться, что переменная l oop %%~K не раскрывается в пустую строку, так как это означало бы, что из файла было прочитано недостаточно частей версии Unity.

Я не знаю не знаю, что означает f1 в конце версии редактора. Таким образом, еще один FOR с параметром /F используется для разбиения строки 4f1 (без usebackq на строку, заключенную в ") на подстроки, используя символы abcdef (шестнадцатеричные символы в нижнем регистре) в качестве разделителей строк и присваиваются указанной переменной l oop L только первая подстрока. Это никогда не должно заканчиваться ошибкой, поэтому переменная окружения UNITY_VERSION определяется с помощью 2019.3.4.

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

for /F "usebackq tokens=2-4 delims=. " %%I in ("%WORKSPACE%\ProjectSettings\ProjectVersion.txt") do (
    if not "%%~K" == "" (
        for /F "delims=abcdef" %%L in ("%%~K") do set "UNITY_VERSION=%%~I.%%~J.%%~L"
        for /D %%M in ("E:\Unity\%%~I.%%~J*") do set "UNITY_FOLDER=%%M"
    )
)

FOR с параметром /D и набором, содержащим * (или ?), получается поиск в указанном каталоге E:\Unity нескрываемого каталога, имя которого начинается с 2019.3. Каждому не скрытому каталогу в E:\Unity, соответствующему шаблону подстановочных знаков 2019.3*, присваивается один за другим полное имя (диск + путь + имя), сначала l oop переменная M и рядом с переменной окружения UNITY_FOLDER. FOR никогда не заключает в себе строку файла / папки в ", поэтому здесь можно использовать %%M, а %%~M не требуется. В этом случае имя папки, присвоенное переменной l oop M, никогда не включается в ". Таким образом, переменная окружения UNITY_FOLDER содержит последнюю папку, соответствующую шаблону подстановочного знака, возвращенному файловой системой с полным путем. Это означает, что для нескольких имен папок, соответствующих шаблону с подстановочными знаками 2019.3*, файловая система определяет, какое имя папки назначено последним для UNITY_FOLDER. NTFS хранит записи каталога в своей главной таблице файлов, отсортированной в локальном порядке c alphabeti c, тогда как записи каталога FAT, FAT32 и exFAT не сортируются в своих таблицах размещения файлов.

Примечание: Если третий номер версии редактора на самом деле не нужен, как это выглядит в соответствии с рассматриваемым кодом, можно также использовать:

for /F "usebackq tokens=2-4 delims=. " %%I in ("%WORKSPACE%\ProjectSettings\ProjectVersion.txt") do (
    if not "%%~J" == "" (
        set "UNITY_VERSION=%%~I.%%~J"
        for /D %%K in ("E:\Unity\%%~I.%%~J*") do set "UNITY_FOLDER=%%K"
    )
)

Две дополнительные проверки выполняются, если код мог успешно определите версию Unity и найдите соответствующую папку Unity.

Командная строка echo в нижней части пакетного файла предназначена только для проверки результата при запуске этого пакетного файла с WORKSPACE, определенным вне пакетного файла. в окне командной строки, и все работает как ожидалось.

Нет необходимости делать каталог рабочей области текущим каталогом до конца пакетного файла, но я добавил код, чтобы сделать это с проверкой при изменении текущий каталог к ​​каталогу рабочего пространства был выполнен действительно успешно .


Проблема 1: Строки аргументов файла / папки не заключены в кавычки

Вывод справки при запуске в командной строке cmd /? поясняется в последнем абзаце на последней странице строка аргумента файла / папки, содержащая пробел или один из этих символов &()[]{}^=;!'+,`~, должна заключаться в прямые двойные кавычки. Поэтому желательно всегда заключать имена файлов / папок без или с путем в ", особенно для одной или нескольких частей, которые динамически определяются переменной окружения или читаются из файловой системы.

Так что не очень хорошо :

cd %WORKSPACE%
IF NOT EXIST %WORKSPACE%\ProjectSettings\ProjectVersion.txt
SET /p TEST=<%WORKSPACE%\ProjectSettings\ProjectVersion.txt

Лучше было бы:

cd "%WORKSPACE%"
IF NOT EXIST "%WORKSPACE%\ProjectSettings\ProjectVersion.txt"
SET /p TEST=<"%WORKSPACE%\ProjectSettings\ProjectVersion.txt"

В кратком справочном выводе при запуске cd /? можно прочитать, что команда CD не интерпретирует символ пробела в качестве разделителя аргументов, как это имеет место для большинства других внутренних команд Windows командный процессор cmd.exe или исполняемых файлов в каталоге %SystemRoot%\System32, которые установлены по умолчанию и также относятся к Windows Commands по данным Microsoft. Но изменить текущий каталог не удастся, если пропустить ", если путь к каталогу случайно содержит амперсанд из-за & вне строки аргумента, заключенного в двойные кавычки, уже интерпретируется cmd.exe как оператор AND перед выполнением CD как описано, например, в моем ответе на одну строку с несколькими командами .

Лучше всего использовать окружение " в каждой строке аргумента, которая может содержать пробел или &()[]{}^=;!'+,`~ или операторы перенаправления <>|, которые должны интерпретироваться командным процессором Windows как буквенные символы строки аргумента. Что ж, квадратные скобки больше не имеют специального значения для командного процессора Windows. [] находятся в списке по историческим причинам, поскольку COMMAND.COM первых версий MS-DOS интерпретировало их не всегда как буквенные символы.


Проблема 2: Использование блока команд для одного команда

Командный процессор Windows предназначен главным образом для

  • открытия пакетного файла,
  • чтения строки из пакетного файла из ранее запомненного байтового смещения или смещение 0 в первой строке,
  • синтаксический анализ и предварительная обработка этой строки,
  • закрытие пакетного файла, когда больше нет строк для чтения,
  • запоминание текущего байтового смещения в пакетном файле,
  • выполнение командной строки.

Вывод справки для команды IF при запуске if /? показывает в верхней части первой страницы общий синтаксис, для которого команда Выполнить при условии, что истина находится в той же строке, что и команда IF . Вывод справки для команды FOR при запуске for /? показывает в верхней части первой страницы общий синтаксис, по которому команда для выполнения на каждой итерации l oop находится в той же строке, что и команда ДЛЯ . Поэтому этот рекомендуемый синтаксис должен использоваться для условий IF и FOR l oop, для которых требуется выполнить только одну команду.

Давайте посмотрим, как Командный процессор Windows интерпретирует следующее IF условие с переменной окружения WORKSPACE, определяемой с помощью C:\Temp:

IF NOT EXIST %WORKSPACE%\ProjectSettings\ProjectVersion.txt (
    EXIT 1
)

Пакетный файл только с этими тремя строками приводит к выполнению :

IF NOT EXIST C:\Temp\ProjectSettings\ProjectVersion.txt (EXIT 1 )

Итак, командный процессор Windows обнаружил, что есть командный блок, начинающийся с (, прочитал больше строк из командного файла до соответствующего ), обнаружил, что командный блок состоит только из одной командной строки, и по этой причине объединили три строки в одну командную строку.

Таким образом, обработка пакетного файла может быть немного ускорена путем записи в пакетный файл:

IF NOT EXIST "%WORKSPACE%\ProjectSettings\ProjectVersion.txt" EXIT /B 1

Тогда требуется меньше инструкций ЦП для выполнения cmd.exe.

IF NOT EXIST "C:\Temp\ProjectSettings\ProjectVersion.txt" EXIT /B 1

Однако использование командного блока всегда позволяет сделать код o fa пакетный файл лучше читается.

Может быть даже полезно поместить весь код пакетного файла или часто выполняемую его часть в один командный блок, если это возможно, чтобы избежать большого количества открытий, чтения, закрытия файлов операции над командным файлом, который иногда оказывает драматическое влияние c на общее время выполнения, как показывает Почему GOTO l oop намного медленнее, чем FOR l oop и дополнительно зависит от источника питания?

См. Также Как Windows интерпретатор команд (CMD.EXE) анализирует сценарии?


Проблема 3: ECHO. может привести к нежелательному поведению

Форум DosTips topi c ECHO. СБОЙ дать текст или пустую строку - вместо этого используйте ECHO / , объясняющий, что ECHO. может не вывести текст или пустую строку. Использование ECHO/ лучше, если следующий символ не ?, а лучший ECHO(.

Символ, отделяющий команду ECHO от строки для вывода, может быть стандартный разделитель аргументов, если гарантировано, что после ECHO есть текст для вывода, например ECHO ProjectVersion.txt = %TEST%.

ECHO/ - это хорошо для вывода пустой строки.

ECHO( лучше всего, если рядом есть ссылка на переменную окружения или ссылка на переменную al oop, в которой не было уверенности, что переменная окружения вообще определена или переменная l oop существует с ненулевой переменной. пустая строка не начинается со знака вопроса.


Проблема 4: Использование SET / P для чтения строки из текстового файла

Для чтения можно использовать set /P первая строка из текстового файла и назначение этой строки переменной среды, как это сделано с помощью:

SET /p TEST=<%WORKSPACE%\ProjectSettings\ProjectVersion.txt

Но текстовый файл должен иметь текст для назначения в переменную окружения в верхней части файла. Пустая строка в верхней части текстового файла приводит к тому, что переменной окружения ничего не назначается, что означает, что если переменная окружения TEST уже определена, ее значение вообще не изменяется, а если переменная окружения TEST не определена до этого он все еще не определен после выполнения SET .

Лучше использовать команду FOR с параметром /F для обработки содержимого текста файл.


Проблема 5: Использование команды EXIT без опции / B

Команда EXIT выходит из командного процесса Windows, который обрабатывает пакетный файл , Это всегда работает, но, тем не менее, следует избегать использования EXIT без опции /B в большинстве пакетных файлов.

Пакетный файл, в котором EXIT без /B без или с кодом завершения выполняется cmd.exe, в результате cmd.exe всегда завершается сам по себе, даже если cmd.exe запускается неявно или явно с опцией /K, чтобы сохранить выполнение командного процесса после завершения выполнения команды, командная строка или пакетный файл и не зависит от иерархии вызовов пакетного файла.

Пакетный файл с EXIT без опции /B поэтому трудно отладить из-за даже при запуске командного файла из окна командной строки вместо двойного щелчка по нему, чтобы увидеть сообщения об ошибках, командный процесс и окно консоли закрываются на cmd.exe достигает командной строки с EXIT .


Проблема 6: Пакетный файл зависит от среды, определенной за пределами

Хороший пакетный файл не зависит от n среда выполнения, определенная вне пакетного файла. Два пакетных файла используют команды с функциями, доступными только с включенными расширениями команд. Расширения команд включены по умолчанию, а задержанное расширение переменных среды по умолчанию отключено, но, тем не менее, лучше, когда пакетный файл определяет саму среду выполнения и восстанавливает предыдущую среду выполнения перед выходом. Это гарантирует, что пакетный файл всегда работает как задумано, даже если другой пакетный файл, вызывающий этот пакетный файл, устанавливает другую среду выполнения.

То есть после @echo off, чтобы убедиться, что ECHO Режим выключен, следующая командная строка должна быть:

setlocal EnableExtensions DisableDelayedExpansion

Тогда пакетный файл определенно выполняется в ожидаемой среде. Команда endlocal должна находиться в конце командного файла для восстановления начальной среды выполнения. Но командный процессор Windows неявно запускает endlocal перед выходом из обработки пакетного файла для каждого выполненного setlocal без выполнения сопоставления endlocal перед выходом из обработки пакетного файла.

Выполнение setlocal /? и endlocal /? приводит к отображению справки этих двух команд. Лучшее объяснение можно найти во второй половине этого ответа с гораздо более подробной информацией о командах SETLOCAL и ENDLOCAL .

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


Проблема 7: Использование букв ADFNPSTXZadfnpstxz в качестве переменной l oop

Справка команды FOR , выводимая при запуске for /?, описывает модификаторы, которые можно использовать при обращении к значению переменной al oop.

    %~I         - expands %I removing any surrounding quotes (")
    %~fI        - expands %I to a fully qualified path name
    %~dI        - expands %I to a drive letter only
    %~pI        - expands %I to a path only
    %~nI        - expands %I to a file name only
    %~xI        - expands %I to a file extension only
    %~sI        - expanded path contains short names only
    %~aI        - expands %I to file attributes of file
    %~tI        - expands %I to date/time of file
    %~zI        - expands %I to size of file
    %~$PATH:I   - searches the directories listed in the PATH
                   environment variable and expands %I to the
                   fully qualified name of the first one found.
                   If the environment variable name is not
                   defined or the file is not found by the
                   search, then this modifier expands to the
                   empty string

Модификаторы можно комбинировать для получения составных результатов:

    %~dpI       - expands %I to a drive letter and path only
    %~nxI       - expands %I to a file name and extension only
    %~fsI       - expands %I to a full path name with short names only
    %~dp$PATH:I - searches the directories listed in the PATH
                   environment variable for %I and expands to the
                   drive letter and path of the first one found.
    %~ftzaI     - expands %I to a DIR like output line

Модификаторы интерпретируются без учета регистра, что означает, что %~FI совпадает с %~fI, тогда как переменная l oop интерпретируется всегда case -чувствительный, что означает, что l oop переменная I интерпретируется иначе, чем l oop переменная i.

Рекомендуется избегать буквы ADFNPSTXZadfnpstxz как переменная l oop, хотя эти буквы также можно использовать как переменную l oop, особенно если ссылка на переменную al oop объединяется со строкой, как в примере ниже.

for %%x in ("1" 2,3;4) do echo %%~xx5 = ?

Вывод в целом (не всегда):

5 = ?
5 = ?
5 = ?
5 = ?

Но при использовании I вывод имеет больше смысла:

for %%I in ("1" 2,3;4) do echo %%~Ix5 = ?

Вывод в этом случае всегда:

1x5 = ?
2x5 = ?
3x5 = ?
4x5 = ?

Можно также использовать другие символы ASCII, кроме букв без специального значения для Windows командного процессора, такого как # или $ в качестве переменной l oop, если не используется FOR с опцией /F, в которой несколько подстрок назначаются нескольким переменным l oop.


Проблема 8: Обработка набора без подстановочных знаков с помощью FOR

Let посмотрим, что в действительности происходит при использовании следующего кода:

setlocal EnableExtensions EnableDelayedExpansion
set "TEST=m_EditorVersion: 2019.3.4f1"
for %%x in (%TEST::= %) do (
    SET "VALUE=%%x"
    SET "UNITY_VERSION=!VALUE:~0,-2!" 
)
endlocal

Подстановка строки %TEST::= % приводит к замене каждого двоеточия пробелом в строке, присвоенной переменной окружения TEST при разборе FOR co Строка mmand со своим блоком команд. Таким образом, строка

m_EditorVersion: 2019.3.4f1

становится

m_EditorVersion  2019.3.4f1

Далее Windows командный процессор заменяет два пробела между m_EditorVersion и 2019.3.4f1 одним пробелом в качестве очистки. Таким образом, набор для обработки с помощью for, наконец, выполняется после синтаксического анализа и предварительной обработки командной строки с помощью for и ее блока команд:

m_EditorVersion 2019.3.4f1

Этот набор не содержит ни *, ни ?. По этой причине команда FOR интерпретирует набор как две простые строки, разделенные пробелом, для присвоения указанной переменной l oop x одну за другой и выполнения команд в блоке команд для этих двух строк два раза. .

На первой итерации m_EditorVersion присваивается переменной окружения VALUE и m_EditorVersi переменной окружения UNITY_VERSION. Это на самом деле не требуется, но FOR запускает две команды еще раз, на этот раз с 2019.3.4f1, назначенным переменной l oop x. Таким образом, на втором этапе l oop итерация 2019.3.4f1 назначается переменной окружения VALUE, а 2019.3.4 - переменной окружения UNITY_VERSION.

UNITY_VERSION определяется окончательно с требуемой строкой, но это можно сделать лучше, как показано и объяснено в верхней части этого ответа.

Мне не совсем понятно, почему в командной строке for появляется сообщение об ошибке:

")" здесь не может быть синтаксически обработано.

Этого не должно происходить, если FOR l oop на m_EditorVersion: 2019.3.4f1 присвоено переменной окружения TEST.

Либо TEST определяется строкой, что приводит к синтаксической ошибке при выполнении второго пакетного файла, хотя это не должно иметь место в соответствии с описанием, либо существует проблема с (, интерпретируемым как начало командный блок и командный процессор Windows не могут найти соответствующий ), который отмечает конец командного блока.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...