Extracting file name from array element in batch

泪湿孤枕 提交于 2019-12-24 07:34:46

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!