问题
I have an environment variable like this
set BINARY[0]=C:\binary.bin
From which I'm trying to extract the full file name
set "x=0"
:binloop
if defined BINARY[%x%] (
call echo %%BINARY[%x%]%%
FOR %%i IN ("%%BINARY[%x%]%%") DO (
set FNAME=%%~nxi
)
set /a "x+=1"
GOTO binloop
)
rem ...
However for some reason, it tries to do:
set FNAME=%BINARY[0]%
instead of
set FNAME=binary.bin
What's wrong with the code and why?
回答1:
Open a command prompt window, run set /?
and read the output help pages explaining when and how to use delayed expansion in a code block for the commands IF and FOR.
%%
in a batch file is interpreted as literal percent character which is the reason why a loop variable in a command executed directly in a command prompt window must be specified with just one percent sign while the same loop in a batch file requires two percent signs on referencing the loop variable.
When the Windows command processor encounters an opening parenthesis which marks the beginning of a command block, it searches for the matching closing parenthesis and replaces all environment variables references with syntax %VariableName%
by the current value of the variable or nothing in case of variable does not exist. Then after the entire command block was parsed the IF or FOR is executed and used is once or more times the already preprocessed command block.
You could use
@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "BINARY[1]=C:\binary1.bin"
set "BINARY[0]=C:\binary0.bin"
set "x=0"
:binloop
if defined BINARY[%x%] (
call echo %%BINARY[%x%]%%
for %%i in ("!BINARY[%x%]!") do (
set FNAME=%%~nxi
set FNAME
)
set /a "x+=1"
goto binloop
)
endlocal
which outputs
C:\binary0.bin
FNAME=binary0.bin
C:\binary1.bin
FNAME=binary1.bin
The command line
call echo %%BINARY[%x%]%%
is something special. This line is preprocessed before execution of command IF to
call echo %BINARY[0]%
respectively on second run to
call echo %BINARY[1]%
By usage of command CALL the single command line is processed like a subroutine or another batch file which means the line is preprocessed once more resulting in execution of
echo C:\binary0.bin
and on second run in execution of
echo C:\binary1.bin
which is the reason why the output is as expected here. But there is no double preprocessing for the environment variable reference in FOR.
Much better would be most likely the following code:
@echo off
setlocal EnableExtensions EnableDelayedExpansion
set "BINARY[1]=C:\binary1.bin"
set "BINARY[0]=C:\binary0.bin"
for /F "tokens=1* delims==" %%I in ('set "BINARY[" 2^>nul') do (
set "FNAME=%%~nxJ"
set FNAME
)
endlocal
The command set
outputs all variables with their name and equal sign and their values which start with the specified string when there is whether parameter /A
or /P
used and the parameter does not contain an equal sign in an alphabetically sorted list. So the output of
set "BINARY[" 2>nul
as used in the command FOR is
BINARY[0]=C:\binary0.bin
BINARY[1]=C:\binary1.bin
which is processed by the FOR loop which splits each line into two strings based on first occurrence of the equal sign because of tokens=1* delims==
. The first string is the variable name assigned to loop variable I
. And the second string is everything after first equal sign assigned to loop variable J
being the next character in ASCII table.
2>nul
is used to suppress the error message output by command SET to STDERR by redirecting it to device NUL if there is no environment variable defined with a name starting with BINARY[
in any case. The redirection operator >
must be escaped with ^
as otherwise command processor would exit batch processing on this line because of 2>nul
resulting in a syntax error on FOR command line at this position.
Note: Because of alphabetically sorted output by command SET the environment variable BINARY[10]
is output after BINARY[0]
and before BINARY[1]
and BINARY[2]
. So if the order is important, the first batch solution is needed or the environment variables are created with number in square brackets have all same number of digits with leading zeros, i.e. 00000, 00001, ..., 00002, 00010, 00011, ...
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
echo /?
endlocal /?
for /?
goto /?
if /?
set /?
setlocal /?
And see also Microsoft article about Using command redirection operators.
来源:https://stackoverflow.com/questions/39041006/extracting-file-name-from-array-element-in-batch