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:
@echo off
setlocal EnableExtensions DisableDelayedExpansion
if not defined WORKSPACE (
echo ERROR: Environment variable WORKSPACE is not defined.
exit /B 1
)
if not exist "%WORKSPACE%ProjectSettingsProjectVersion.txt" (
echo ERROR: File "%WORKSPACE%ProjectSettingsProjectVersion.txt" does not exist.
exit /B 1
)
set "UNITY_FOLDER="
set "UNITY_VERSION="
for /F "usebackq tokens=2-4 delims=. " %%I in ("%WORKSPACE%ProjectSettingsProjectVersion.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%ProjectSettingsProjectVersion.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.
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.
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.
The two environment variables UNITY_FOLDER
and UNITY_VERSION
are deleted if defined by chance outside the batch file.
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.
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.
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
.
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
.
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.
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.
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 I
, J
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 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
.
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.
for /F "usebackq tokens=2-4 delims=. " %%I in ("%WORKSPACE%ProjectSettingsProjectVersion.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_FOLDER
. FOR 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.
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%ProjectSettingsProjectVersion.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.
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.
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
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.
So not good are the following command lines:
cd %WORKSPACE%
IF NOT EXIST %WORKSPACE%ProjectSettingsProjectVersion.txt
SET /p TEST=<%WORKSPACE%ProjectSettingsProjectVersion.txt
Better would be using:
cd "%WORKSPACE%"
IF NOT EXIST "%WORKSPACE%ProjectSettingsProjectVersion.txt"
SET /p TEST=<"%WORKSPACE%ProjectSettingsProjectVersion.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 <a href="https://stackoverflow.c