两个几乎相同的批处理脚本之一中的语法错误:“)”

Q:

I am trying to set up a Jenkins server for automatic Unity builds.
我正在尝试设置用于自动统一构建的Jenkins服务器。

Therefore I have written two (in my eyes) basically identical batch scripts.
因此,我写了两个(在我眼中)基本相同的批处理脚本。

Both scripts are run as build steps by Jenkins via an Execute Windows batch command step using
Jenkins通过Execute Windows batch command步骤使用两个脚本作为构建步骤运行

Command: E:\unityImport.bat
命令: E:\unityImport.bat

and after that a second Execute Windows batch command step using
在那之后,第二个Execute Windows batch command步骤使用

Command: E:\unityBuild.bat
命令: E:\unityBuild.bat

They both have the same beginning as I need to gather some file paths and in particular the project’s unity version. So in both scripts I use exactly the same way of parsing and string splitting the project version. The only thing that is different between them is that the first one starts Unity and imports a dedicated unitypackage (which hold the method to execute in the next step) into the project while the second one again starts Unity to perform the actual build (unfortunately it didn’t work in one single go … Unity seems to try to execute the method before the unitypackage is imported).
它们都与我需要收集一些文件路径相同的开始,尤其是项目的统一版本。因此,在两个脚本中,我都使用完全相同的解析和字符串将项目版本拆分。它们之间唯一不同的是,第一个启动Unity并导入专用unitypackage (将下一步执行的方法)进入项目,而第二个则再次开始Unity以执行实际构建(不幸的是,它没有一个单一的工作… Unity似乎在导入unitypackage之前尝试执行该方法)。

However the second script always fails with a syntax error
但是,第二个脚本总是失败的语法错误

“)” cannot be processed syntactically here.
”)“在这里不能句法处理。


What I try to achieve is
我试图实现的是

  1. Read out the content of the file %WORKSPACE%\ProjectSettings\ProjectVersion.txt
    读取文件的内容 %WORKSPACE%\ProjectSettings\ProjectVersion.txtSET /p TEST=<%WORKSPACE%\ProjectSettings\ProjectVersion.txt The content of %TEST% usually looks like e.g.
    %TEST%的含量通常看起来像m_EditorVersion: 2019.3.4f1 and ECHO. ProjectVersion.txt = %TEST% looks like
    和 ECHO. ProjectVersion.txt = %TEST% 看起来像ProjectVersion.txt = m_EditorVersion: 2019.3.4f1
  2. string split in order to only take the last part containing the version number
    字符串拆分,以便仅拿最后一部分包含版本号for %%x in (%TEST::= %) do ( SET "VALUE=%%x" SET "UNITY_VERSION=!VALUE:~0,-2!" ) so %UNITY_VERSION% usually contains e.g. 2019.3.4. I don’t split more away because there are also Unity version with two digits like e.g. 2018.4.18
    因此, %UNITY_VERSION%通常包含EG 2019.3.4 。我不会分开更多,因为还有两个数字,例如EG 2018.4.18
  3. string split on the . in order to only get the major release number
    字符串在.为了仅获取主要发行号for /f "tokens=1,2 delims=." %%a in ("%UNITY_VERSION%") do ( SET "A=%%a" SET "B=%%b" ) SET "UNITY_VERSION=%A%.%B%" which results in %UNITY_VERSION% being e.g. 2019.3
    导致%UNITY_VERSION%为EG 2019.3
  4. Finally search in all installed Unity versions if the required version is present
    最后在所有已安装的统一版本中搜索是否存在所需版本set "UNITY_FOLDER=" for /f "delims=" %%a in ('dir /b E:\Unity\%UNITY_VERSION%*') do ( set "UNITY_FOLDER=%%a" ) after this we either found a valid Unity installation folder for the given version or not.
    之后,我们要么找到给定版本的有效的Unity安装文件夹。

So here are the scripts.
所以这是脚本。

Import (This works as expected)
导入(这是按预期起作用的)

@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

Build (This fails with a syntax error I will mark with REM HERE IT BREAKS! ... which is not present in the actual script)
构建(这是通过语法错误失败,我将在此处标记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%

...

I don’t think the rest matters since as said What I see in the console it already breaks after e.g.
我认为其余的都不重要,因为正如我在控制台中看到的内容,例如

ProjectVersion.txt = 2019.3.4f1

“)” cannot be processed syntactically here.
”)“在这里不能句法处理。


Does anyone see the mistake or is there maybe something with Jenkins that makes the second script fail with a syntax error even though as far as I see they are basically identical?
有没有人看到这个错误,或者詹金斯是否有一些使第二个脚本失败的语法错误,即使据我所知,它们基本上是相同的?

A:

There are multiple small issues with the code which I explain one after the other below my suggestion for the batch file.
代码有多个小问题,我对批处理文件的建议下方进行了一个小问题。

The task to get the UNITY_FOLDER according to UNITY_VERSION as defined in file ProjectVersion.txt can be done more efficient by using the following code:
根据文件ProjectVersion.txt中定义的UNITY_VERSION获取UNITY_FOLDER的任务。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

This batch file first sets up the execution environment required for this batch file using command SETLOCAL.
该批处理文件首先使用命令setLocal设置此批处理文件所需的执行环境。

The existence of the environment variable WORKSPACE is verified next by the batch file. This environment variable should be defined by Jenkins outside this batch file. An error message is output on missing definition of this important environment variable.
批处理文件接下来验证了环境变量WORKSPACE的存在。此环境变量应由此批处理文件之外的Jenkins定义。对于此重要环境变量的缺少定义,输出错误消息。

Then the existence of the text file is checked with printing an error message if not existing and exiting batch file with exit code 1.
然后,如果不存在,则使用打印错误消息检查文本文件的存在,并在退出代码1中退出批处理文件。

The two environment variables UNITY_FOLDER and UNITY_VERSION are deleted if defined by chance outside the batch file.
如果批处理文件之外的机会定义了两个环境变量UNITY_FOLDERUNITY_VERSION ,则将删除。

Next the text file is processed which should contain only one non-empty line with the data of interest. Otherwise it would be necessary to change the code to evaluate also the first substring if being equal m_EditorVersion: before execution of the other commands.
接下来,处理文本文件,该文件应仅包含一条非空线与感兴趣的数据。否则,有必要更改代码,以评估第一个子字符串,如果是相等的m_EditorVersion:执行其他命令之前。

FOR with option /F interprets a set enclosed in " by default as string to process. But in this case the string in " should be interpreted as full qualified file name of the file of which contents should be processed line by line by FOR. For that reason the option usebackq is used to get the wanted file contents processing behavior.
对于选项/F解释了"默认情况下为字符串要处理的字符串"中包含的集合。但是在这种情况下,应将字符串解释为应将内容的完整文件名解释为应按行处理中的内容的文件。因此,选项usebackq用于获取所需的文件内容处理行为。

FOR ignores always empty lines on processing the contents of a file. So it would not matter if the text file contains at top one or more empty lines.
对于处理文件内容时,忽略总是空的行。因此,文本文件是否包含在顶部或多个空线上都不重要。

FOR splits up a line by default into substrings using normal space and horizontal tab character as string delimiters. If the first space/tab delimited string starts with a semicolon being the default end of line character after removing all leading spaces/tabs, the line would be also ignored by FOR like an empty line. Finally just the first space/tab delimited string would be assigned to the specified loop variable I.
默认情况下,使用正常空间和水平选项卡字符作为字符串定界符将线路分配到子字符串中。如果第一个空格/选项卡划界字符串从删除所有领先空间/选项卡后的半olon开始是线字符的默认末端,则该行也将被忽略例如一个空行。最后,仅将第一个空格/选项卡划界字符串分配给指定的循环变量I 。

This default line processing behavior is not wanted here because of getting just m_EditorVersion: assigned to the specified loop variable I is not enough. For that reason the option delims=.  is used to get the line split up on dots and spaces. The option tokens=2-4 informs FOR that the second space/dot delimited substring 2019 should be assigned to loop variable I, the third space/dot delimited substring 3 to next loop variable J which is the next character in the ASCII table and the fourth space/dot delimited substring 4f1 to next but one loop variable K.
由于仅获得m_EditorVersion:分配给指定的循环变量I还不够,因此不需要此默认行处理行为。因此,选项delims=.用于将线路分开在点和空间上。选项tokens=2-4告知,应将2019第二个空间/点划界划界分配给循环变量I ,第三个空间/点界定子字符串3到下一个循环变量J这是ASCII表中的下一个字符,第四空间/点划界子字符带4f1到接下来,但一个循环变量K 。

It is important here to specify delims=.  at end of the options argument string with the space character as last character because of the space character is otherwise interpreted as an options separating character to ignore like the space between usebackq and tokens=2-4 and the space between tokens=2-4 and delims=. . In fact it would be also possible to write the options without spaces like "usebackqtokens=2-4delims=. ", but that makes the argument string with the options difficult to read.
在这里指定delims=.在选项的末尾,以空格字符为最后一个字符的选项参数字符串,因为空间字符被解释为分隔字符的选项,以忽略usebackqtokens=2-4之间的空间,而tokens=2-4之间的空间delims=. 。实际上,也可以编写没有空间的选项,例如"usebackqtokens=2-4delims=. "但是,这使得与选项难以读取的参数字符串。

The default end of line definition eol=; can be kept here because of the line with the unity version in ProjectVersion.txt does not have a semicolon after 0 or more spaces/dots and is never ignored for that reason.
行定义eol=;可以在此处保留在此处,因为在ProjectVersion.txt中使用Unity版本。TXT在0或更多空间/点之后没有半隆,因此从未被忽略。

FOR runs the commands in the command block on having found in line at least the second space/dot delimited string assigned to loop variable I, i.e. a non-empty string is assigned to specified loop variable I. But the commands should be executed only if all three parts of the unity version were determined by FOR and assigned to the loop variables IJ and K. Therefore a simple string comparison is made to verify that loop variable value reference %%~K does not expand to an empty string as that would mean not having read enough parts of unity version from the file.
对于在命令块中运行的命令,至少在行中找到的命令至少在循环变量I第二个空间/点划界字符串中,即将非空字符串分配给指定的loop变量I 。但是,只有在统一版本的所有三个部分都由for并分配给循环变量I , JK所有三个部分时,才应执行命令。因此,进行了简单的字符串比较来验证循环变量值参考%%~K不会扩展到空字符串,因为这意味着没有从文件中读取足够的统一版本的部分。

I don’t know what f1 at end of the editor version means. So one more FOR with option /F is used to split the string 4f1 (no usebackq on string enclosed in ") into substrings using the characters abcdef (lower case hexadecimal characters) as string delimiters and get assigned to specified loop variable L just the first substring. That should never fail and so environment variable UNITY_VERSION is defined with 2019.3.4.
我不知道编辑器版本的f1是什么意思。因此,使用选项/F另外一个用于使用字符abcdef (较低的情况十六进制字符)将字符串4f1 (在字符串上的usebackq no usebackq划分为字符串"为字符串定界L底带永远不会失败,因此环境变量UNITY_VERSION2019.3.4定义。

The third FOR is executed inside the second FOR although it could be also outside because of not referencing loop variable L. So the following code could be also used here with the same result.
第三是在第二个内部执行的尽管它也可能在外部,因为没有引用循环变量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 with option /D and a set containing * (or ?) results in searching in specified directory E:\Unity for a non-hidden directory of which name starts with 2019.3. Each non-hidden directory in E:\Unity matching the wildcard pattern 2019.3* is assigned one after the other with full qualified name (drive + path + name) first to loop variable M and next to environment variable UNITY_FOLDERFOR never encloses itself a file/folder string in " which is the reason why %%M can be used here and %%~M is not necessary. The folder name assigned to loop variable M is never enclosed in " in this case. So the environment variable UNITY_FOLDER contains the last folder matching the wildcard pattern returned by the file system with full path. This means on multiple folder names matching the wildcard pattern 2019.3* that the file system determines which folder name is assigned last to UNITY_FOLDER. NTFS stores directory entries in its master file table sorted in a local specific alphabetic order while FAT, FAT32 and exFAT store directory entries unsorted in their file allocation tables.
对于选项/D和一个包含* (或? )的集合会导致在指定的目录e中搜索E:\Unity ,以获取哪个名称的非隐藏目录从2019.3开始。 E:\Unity中的每个非隐藏目录与通配符模式2019.3*匹配,将一个接一个地分配给一个完整的合格名称(Drive + Path + Path + name),首先是循环变量M ,而Environment varibe varible UNITY_FOLDER则将其分配。因为永远不要将自己包装在"这是可以在此处使用%%M M ,也不" %%~M 。因此,环境变量UNITY_FOLDER包含了最后一个文件夹,该文件夹匹配文件系统返回的通配符与完整路径。这意味着在多个文件夹名称上匹配通配符模式2019.3*文件系统确定最后分配给UNITY_FOLDER文件夹名称。 NTFS将目录条目存储在其主文件表中,以本地特定的字母顺序排序,而FAT,FAT32和EXFAT商店目录条目在其文件分配表中未分类。

Note: If the third number of editor version is not really needed as it looks like according to the code in question, it would be also possible to use:
注意:如果不需要第三个编辑器版本,因为根据所讨论的代码,它也可以使用:

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"
    )
)

Two additional checks are made if the code could successfully determine unity version and find a matching unity folder.
如果代码可以成功确定Unity版本并找到匹配的Unity文件夹,则进行了两次检查。

The echo command line at bottom of the batch file is just for verification of the result on running this batch file with WORKSPACE defined outside the batch file in command prompt window and everything worked as expected.
批处理文件底部的echo命令行只是为了验证结果在运行此批处理文件时使用命令提示符窗口中批处理文件外定义的WORKSPACE空间,并且一切都按预期工作。

There is no need to make the workspace directory the current directory up to end of the batch file, but I added the code to do that with the verification if changing the current directory to workspace directory was done really successfully.
无需将工作区目录的当前目录到批处理文件的末尾,但是如果将当前目录更改为工作空间目录的目录确实成功完成了,则我添加了代码来进行验证。


Issue 1: File/folder argument strings not enclosed in quotes
问题1:文件/文件夹参数字符串未包装在报价中

The help output on running in a command prompt cmd /? explains with last paragraph on last page that a file/folder argument string containing a space or one of these characters &()[]{}^=;!'+,`~ requires surrounding straight double quotes. So it is advisable to always enclose file/folder names without or with path in ", especially on one or more parts being dynamically defined by an environment variable or being read from file system.
命令提示符cmd /?用上一页上的最后一段说明文件/文件夹参数字符串,其中包含一个空间或其中一个字符&()[]{}^=;!'+,`~需要直接双引号。因此,建议始终将文件/文件夹名称封闭,而无需或使用"中的情况,尤其是在一个或多个零件上由环境变量动态定义或从文件系统中读取。

So not good are the following command lines:
因此,以下命令行不好:

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

Better would be using:  更好的是使用:

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

It can be read in short help output on running cd /? that the command CD does not interpret a space character as argument separator like it is the case for most other internal commands of Windows command processor cmd.exe or executables in directory %SystemRoot%\System32 which are installed by default and also belong to the Windows commands according to Microsoft. But changing the current directory fails on omitting " if the directory path contains by chance an ampersand because of & outside a double quoted argument string is interpreted already by cmd.exe as AND operator before execution of CD as described for example in my answer on single line with multiple commands.
可以在运行cd /?命令CD不会将空格字符解释为参数分离器,就像Windows命令处理器cmd.exe的大多数其他内部命令是这种情况,或在目录%SystemRoot%\System32中的可执行文件,该命令默认安装,也属于Windows根据Microsoft的命令。但是更改当前目录在省略"如果目录路径在偶然的偶然性中包含and and and and and and of and of and of and and of and and of and and of and of and & of and of double引用的参数字符串已由cmd.exe解释为运算符在执行CD之前,则如我的回答中所述。与多个命令排列

It is best to use surrounding " on every argument string which could contain a space or &()[]{}^=;!'+,`~ or the redirection operators <>| which should be interpreted by Windows command processor as literal characters of an argument string. Well, the square brackets do not have anymore a special meaning for Windows command processor. [] are in the list for historical reasons as COMMAND.COM of the first versions of MS-DOS interpreted them not always as literal characters.
最好使用周围的"在每个参数字符串上,其中可能包含一个空间或&()[]{}^=;!'+,`~或重定向操作员<>|应由Windows命令命令处理器解释作为参数COMMAND.COM []字符字符,正方形括号不再具有Windows命令处理器的特殊含义。作为字面角色。


Issue 2: Usage of a command block for a single command
问题2:单个命令的命令块的用法

The Windows command processor is designed primary for
Windows命令处理器是为主要设计的

  • opening a batch file,  打开批处理文件,
  • reading a line from the batch file from previously remembered byte offset or offset 0 on first line,
    从先前记住的字节偏移或第一行中的偏移0中读取批处理文件的行,
  • parsing and preprocessing this line,
    解析和预处理这一行,
  • closing the batch file on no more lines to read,
    在没有更多的行上关闭批处理文件,
  • remembering current byte offset in batch file,
    记住当前的字节偏移批处理文件,
  • executing the command line.
    执行命令行。

The help output for command IF on running if /? shows at top of first page the general syntax on which the command to execute on condition being true is on the same line as the command IF. The help output for command FOR on running for /? shows at top of first page the general syntax on which the command to execute on each loop iteration is on the same line as the command FOR. Therefore this recommended syntax should be used for an IF condition and a FOR loop on which just one command needs to be executed.
如果/ if /?在第一页的顶部显示一般语法,条件为true的命令与命令在同一行上。for /?在第一页的顶部显示一般语法,每个循环迭代在该命令上执行的命令与命令在同一行上。因此,该建议的语法应用于IF条件和仅需要执行一个命令循环。

Let us look how Windows command processor interprets the following IF condition with environment variable WORKSPACE being defined with C:\Temp:
让我们查看Windows命令处理器如何解释以下条件,该WORKSPACE使用C:\Temp temp:\ temp:\ temp:

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

A batch file with just those three lines results in execution of:
只有这三行的批处理文件导致执行:

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

So Windows command processor detected that there is a command block starting with (, read more lines from the batch file up to matching ), found out that the command block consists of only one command line, and merged the three lines together to one command line for that reason.
因此,Windows命令处理器检测到有一个命令块以(从批处理文件中读取更多行到匹配) ,发现命令块仅由一个命令行组成,并将三行合并到一条命令行中因此。

So the batch file processing could be speed up a very little bit by writing in the batch file:
因此,通过在批处理文件中写入批处理文件处理可以加快速度:

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

Then less CPU instructions are needed to get executed by cmd.exe.
然后,需要更少的CPU指令才能通过cmd.exe执行。

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

However, the usage of a command block is always possible to make the code of a batch file better readable.
但是,始终可以使用命令块的使用方法使批处理文件的代码更好。

It could be even useful to put entire code of a batch file or a part of it executed often into one command block if that is possible to avoid lots of file open, read, close operations on the batch file which has sometimes a dramatic effect on total execution time as demonstrated by Why is a GOTO loop much slower than a FOR loop and depends additionally on power supply?
如果可以避免打开大量文件,读取,在批处理文件上关闭操作,则将批处理文件的整个代码或其一部分经常执行可能很有用,有时会对总的执行时间为何goto循环比循环慢得多,并且还取决于电源吗?

See also How does the Windows Command Interpreter (CMD.EXE) parse scripts?
另请参阅Windows命令解释器(CMD.EXE)如何解析脚本?


Issue 3: ECHO. could result in unwanted behavior
问题3:回声。可能导致不必要的行为

The DosTips forum topic ECHO. FAILS to give text or blank line – Instead use ECHO/ explains that ECHO. could fail to output text or an empty line. The usage of ECHO/ is better if the next character is not ? and best is ECHO(.
DOSTIPS论坛主题回声。无法给出文本或空白行 – 而是使用回声/解释该ECHO.可能无法输出文本或空线。如果下一个字符不是,则使用ECHO/更好?最好的是ECHO( 。

The character separating the command ECHO from the string to output can be the standard argument separator space if there is guaranteed that there is a text to output after ECHO  like on ECHO ProjectVersion.txt = %TEST%.
如果保证在ECHO之后有一个文本,则将命令回声与字符串与输出分开的字符可以是标准参数分隔符空间 ECHO ProjectVersion.txt = %TEST% 。

ECHO/ is good to output an empty line.
ECHO/很好地输出一个空线路。

ECHO( is best if there is next an environment variable reference or a loop variable reference on which was not made sure before that the environment variable is defined at all or the loop variable exists with a non-empty string not starting with a question mark.
ECHO(最好是下一个环境变量引用或循环变量引用,而在该环境变量完全定义之前,该引用是完全定义的,或者存在循环变量,而非空字符串则不以问号开头。


Issue 4: Usage of SET /P to read a line from a text file
问题4:set /p的用法从文本文件中读取一行

It is possible to use set /P to read the first line from a text file and assign this line to an environment variable as done with:
可以使用set /P从文本文件中读取第一行,并将此行分配给以下情况:

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

But the text file must have the text to assign to the environment variable at top of the file. An empty line at top of the text file results in assigning nothing to the environment variable which means that if the environment variable TEST is defined already, its value is not changed at all, and if the environment variable TEST is not defined before, it is still not defined after execution of SET.
但是文本文件必须具有文本以分配到文件顶部的环境变量。文本文件顶部的一个空行导致对环境变量的分配没有分配,这意味着如果已经定义了环境变量TEST ,则根本不更改其值,并且如果以前未定义环境变量TEST ,则它是在执行设置后仍未定义。

It is better to use the command FOR with option /F to process the contents of a text file.
最好将命令与选项/F一起处理文本文件的内容。


Issue 5: Usage of command EXIT without option /B
问题5:无需选项 /b的命令退出的使用

The command EXIT exits the Windows command process which is processing the batch file. It always works, but it should be nevertheless avoided to use EXIT without option /B in most batch files.
命令退出正在处理批处理文件的Windows命令进程。它总是有效的,但是应该避免在大多数批处理文件中使用没有选项/B退出

A batch file on which EXIT without /B without or with an exit code is executed by cmd.exe results in cmd.exe always terminating itself, even on cmd.exe being started implicit or explicit with option /K to keep the command process running after finishing execution of a command, command line or batch file and independent on the batch file calling hierarchy.
cmd.exe执行的无/B或没有退出代码的批量文件在cmd.exe中执行始终终止自身,即使在cmd.exe上也启动了intemit或option /K以使命令流程运行以保持命令 /k完成执行命令,命令行或批处理文件后,并独立于调用层次结构的批处理文件。

A batch file with EXIT without option /B is therefore hard to debug because of even on running the batch file from within a command prompt window instead of double clicking on it to see error messages, the command process and the console window are closed on cmd.exe reaches the command line with EXIT.
因此,由于从命令提示符窗口中运行批处理文件,而不是双击它以查看错误消息,命令进程和控制台窗口在cmd.exe上关闭,因此很难进行无需选项/B批处理文件,因此很难进行调试。 cmd.exe带有出口到达命令行。


Issue 6: Batch file depends on environment defined outside
问题6:批处理文件取决于外面定义的环境

A good designed batch file does not depend on an execution environment defined outside of the batch file. The two batch files use commands with features available only with enabled command extensions. The command extensions are enabled by default and delayed environment variable expansion is disabled by default, but it is nevertheless better when a batch file defines itself the execution environment and restores the previous execution environment before exiting. This makes sure that the batch file always works as designed even if another batch file calling this batch file sets up a different execution environment.
设计良好的批处理文件不取决于在批处理文件之外定义的执行环境。两个批处理文件使用命令,其中仅具有启用命令扩展名的功能。默认情况下,默认情况下启用了命令扩展名,默认情况下禁用了延迟的环境变量扩展,但是当批处理文件定义自己的执行环境并在退出之前恢复先前的执行环境时,它会更好。这要确保批处理文件始终按设计工作,即使另一个批处理文件调用此批处理文件设置了不同的执行环境。

So after @echo off to make sure that the ECHO mode is turned off the next command line should be:
因此,在@echo off之后,以确保将ECHO模式关闭,下一个命令行应该是:

setlocal EnableExtensions DisableDelayedExpansion

Then the batch file is definitely executed in the expected environment. The command endlocal should be at end of the batch file to restore initial execution environment. But Windows command processor implicitly runs endlocal before exiting the processing of a batch file for every executed setlocal without the execution of a matching endlocal before exiting the batch file processing.
然后,批处理文件绝对是在预期的环境中执行的。命令endlocal应在批处理文件的末尾,以恢复初始执行环境。但是,Windows命令处理器隐式运行endlocal ,然后在每个执行的setlocal的批处理文件的处理之前,而无需执行匹配的endlocal然后才退出批处理文件处理。

The executions of setlocal /? and endlocal /? result in displaying the help of those two commands. A better explanation can be found in this answer with much more details about the commands SETLOCAL and ENDLOCAL.
setlocal /?endlocal /?导致显示这两个命令的帮助。可以在此答案中找到更好的解释,并提供有关命令凝位内置环境的更多详细信息。

The usage of setlocal at top of a batch file to set up the required execution environment and endlocal at bottom of the batch file to restore initial execution environment must be just done wisely in case of a batch file should return results via environment variables to initial execution environment like a parent batch file which called the currently executed batch file.
在批处理文件的顶部使用setlocal以设置所需的执行环境,并且在批处理文件底部的endlocal以恢复初始执行环境,如果批处理文件应通过环境变量返回结果,以恢复初始执行环境像父批处理文件一样的环境,该文件称为当前执行的批处理文件。


Issue 7: Usage of letters ADFNPSTXZadfnpstxz as loop variable
第7期:字母的使用ADFNPSTXZadfnpstxz作为循环变量

The help of command FOR output on running for /? describes the modifiers which can be used on referencing the value of a loop variable.
运行for /?描述可用于引用循环变量值的修饰符。

   %~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

The modifiers can be combined to get compound results:
修饰符可以组合以获得复合结果:

   %~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

The modifiers are interpreted case-insensitive which means %~FI is the same as %~fI while the loop variable is interpreted always case-sensitive which means loop variable I is interpreted different to loop variable i.
修饰符被解释为对病例的不敏感,这意味着%~FI%~fI相同,而循环变量被解释总是对病例敏感的,这意味着循环变量I与循环变量i不同。

It is advisable to avoid the letters ADFNPSTXZadfnpstxz as loop variable although these letters can be also used as loop variable, especially if a loop variable reference is concatenated with a string as in the batch file command line example below.
建议避免字母ADFNPSTXZadfnpstxz作为循环变量,尽管这些字母也可以用作循环变量,尤其是在循环变量引用与字符串串联相连的情况下,如下面的批处理文件命令行中所示。

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

The same example for execution directly in a command prompt window:
直接在命令提示室窗口中执行的同一示例:

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

The output is in general (not always):
输出通常是(并非总是):

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

But the output makes more sense on using I in a batch file:
但是输出在批处理文件中使用I更有意义:

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

The same command line for execution directly in a command prompt window:
在命令提示符窗口中直接执行的同一命令行:

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

The output is in this case always:
在这种情况下,输出始终是:

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

So it is not possible to use ADFNPSTXZadfnpstxz as loop variable if
因此,如果不可能将ADFNPSTXZadfnpstxz用作循环变量,如果

  1. the loop variable value is referenced with a modifier which means the loop variable value reference begins with %~ (command prompt window) or %%~ (batch file) and
    循环变量值用修饰符引用,这意味着循环变量值参考以%~ (命令提示窗口)或%%~ (批处理文件)和
  2. the loop variable value reference is concatenated with a string of which first character is identical to the letter used for the loop variable.
    循环变量值参考与一个字符串相连,该字符串与用于循环变量的字母相同。

So working fine in a command prompt window are:
因此,在命令提示符窗口中正常工作是:

for %x in (1 2,3;4) do @echo %xx5 = ?      & rem Condition 1 is not true.
for %n in ("1" 2,3;4) do @echo %~nx5 = ?   & rem Condition 2 is not true.
for %x in ("1" 2,3;4) do @echo %~x+5 = ?   & rem Condition 2 is not true.

However, the readability is not good on using a letter which can be used to reference the string value assigned to a loop variable with a modifier.
但是,使用字母可读性不好,该字母可用于引用带有修饰符的循环变量分配的字符串值。

Readability example for usage in a command prompt window:
在命令提示符窗口中使用的可读性示例:

for %i in (*) do @echo %~si
for %f in (*) do @echo %~sf
for %i in (*) do @echo %~sni
for %f in (*) do @echo %~snf

In this case i and f work both and the output is the same independent on usage of i or f. But it is easier to see what are the modifiers (s and n) and what is the loop variable on using i and not f for the loop variable.
在这种情况下, if都可以使用,并且输出在使用if的使用情况下是相同的。但是,更容易看到修饰符( sn )是什么,而对于循环变量,使用i而不是f循环变量是什么。

It is also possible to use other ASCII characters than letters with no special meaning for Windows command processor like # as loop variable if not using FOR with option /F on which multiple substrings are assigned to multiple loop variables.
除了对Windows命令处理器没有特殊含义的字母以外,也可以使用其他ASCII字符#例如#loop变量,如果不使用with with with with with option /F将多个子字符串分配给多个循环变量。


Issue 8: Processing of a set without wildcards by FOR
第8期:无需通配符的设置的处理

Let us look on what really happens on using the following code:
让我们查看使用以下代码真正发生的事情:

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

The string substitution %TEST::= % results in replacing each colon by a space in the string assigned to environment variable TEST on parsing the FOR command line with its command block. So the string
字符串替换%TEST::= %导致通过分配给环境变量TEST字符串中的空间来代替每个结肠,并用其命令块解析for命令行。所以字符串

m_EditorVersion: 2019.3.4f1

becomes  变成

m_EditorVersion  2019.3.4f1

Next Windows command processor replaces the two spaces between m_EditorVersion and 2019.3.4f1 by a single space as cleanup. So the set to process by for is finally after parsing and preprocessing the command line with for and its command block:
下一个Windows命令处理器将m_EditorVersion2019.3.4f1之间的两个空间替换为清理空间。因此,通过解析和预处理for行及其命令块的预处理后,要处理for集合最终是:

m_EditorVersion 2019.3.4f1

This set contains neither * nor ?. For that reason the command FOR interprets the set as two simple space separated strings to assign to specified loop variable x one after the other and execute the commands in command block two times for those two strings.
此组既不包含*也不包含? 。因此,命令将集合解释为两个简单空间分开的字符串,将其分配给指定的循环变量x一个接一个地,并在命令块中执行两次命令块中的命令。

On first iteration m_EditorVersion is assigned to environment variable VALUE and m_EditorVersi to the environment variable UNITY_VERSION. That is not really wanted, but FOR runs the two commands once more, this time with 2019.3.4f1 assigned to the loop variable x. So on second loop iteration 2019.3.4f1 is assigned to the environment variable VALUE and 2019.3.4 to the environment variable UNITY_VERSION.
在第一次迭代中, m_EditorVersion被分配给环境变量VALUE ,将m_EditorVersi分配给环境变量UNITY_VERSION 。这不是真正想要的,但是对于再次运行两个命令,这次是分配给循环变量x 2019.3.4f1 。因此,在第二循环迭代中, 2019.3.4f1被分配给环境变量VALUE ,而2019.3.4则将环境变量UNITY_VERSION分配给2019.3.4。

UNITY_VERSION is defined finally with the wanted string, but that could be done better as shown and explained at top of this answer.
UNITY_VERSION最终用所需的字符串定义,但是可以做得更好,如此答案的顶部所示和解释。

It is not really clear for me why the for command line results in the error message:
对我而言并不清楚为什么命令for会导致错误消息:

“)” cannot be processed syntactically here.
”)“在这里不能句法处理。

That should not happen ever for this FOR loop on m_EditorVersion: 2019.3.4f1 being assigned to environment variable TEST.
对于m_editorversion上的循环m_EditorVersion: 2019.3.4f1被分配给环境变量TEST ,这不应该发生。

Either TEST is defined with a string resulting in the syntax error on execution of the second batch file although that should not be the case according to the description or there is an issue with ( interpreted as beginning of a command block and the Windows command processor fails to find the matching ) which marks the end of the command block.
用字符串定义任何一个TEST导致语法错误在执行第二个批次文件时,尽管不应该根据描述是这种情况,否则存在问题(解释为命令块的开始,Windows命令处理器失败找到匹配) ,标记了命令块的末尾。

912sy.com下载资源均来源于网络,仅供学习和参考使用,版权归原作者所有,勿作商业用途,请在下载后24小时之内自觉删除。
本站发布的内容若无意中侵犯到您的权益,请联系我们,本站将在一个工作日内删除。如遇到任何问题请联系客服QQ:2385367137
912sy » 两个几乎相同的批处理脚本之一中的语法错误:“)”