How to expand two local variables inside a for loop in a batch file

前端 未结 3 1586
天涯浪人
天涯浪人 2020-12-22 05:10

I\'m trying to write a script which should read from a list of files and copy each item to a new location, following the same structure.

Originally, this was intende

相关标签:
3条回答
  • 2020-12-22 05:43

    This answer elaborates on expanding "nested variables" only:

    In short, use this:

    call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%"
    

    Escaping ^! or using call and !! does not work to expand nested variables with delayed expansion.

    Pipes cannot help either as they initialise new cmd instances for both sides, which use their own separate environments.


    Let us take a look at nested variables using immediate expansion first:

    call set "dest_file_dir=%%dest_file_full:%dest_file_filename%=%%"
    

    call introduces a second parsing phase, which is required to resolve the nesting. The call command receives a partially expanded command line, like this (supposing %dest_file_filename% expands to config.php, for example):

    set "dest_file_dir=%dest_file_full:config.php=%"
    

    This is nothing but the normal sub-string replacement syntax with immediate expansion, which is expanded during the said second parsing phase.

    Note: This approach fails in case the value of dest_file_filename begins with %, ~, or * (but this is a forbidden character for file names anyway), or if it contains =! In addition, if present, " could cause trouble too (but such are also not allowed in file names)!


    Now let us check out a similar approach but with delayed expansion enabled:

    According to the post How does the Windows Command Interpreter (CMD.EXE) parse scripts?, call introduces a second parsing phase, but this does not include delayed expansion but immediate expansion only; so delayed expansion is only done during the first parsing phase.

    Similar to the above solution using immediate expansion, we need to make sure that the search and replace strings of the sub-string replacement syntax are expanded during the first parsing phase, and that the overall expression is handled by the second one; so let us use this:

    call set "dest_file_dir=%%dest_file_full:!dest_file_filename!=%%"
    

    So the call command receives the following partially expanded command line:

    set "dest_file_dir=%dest_file_full:config.php=%"
    

    Again this constitutes the sub-string replacement syntax with immediate expansion.

    Note: This approach fails in case the value of dest_file_filename begins with %, ~, or * (but this is a forbidden character for file names anyway), or if it contains =! In addition, if present, " could cause trouble too (but such are also not allowed in file names)!


    An alternative method to introduce a second parsing phase needed for expanding nested variables is to use a for loop, like this:

    for /F delims^=^ eol^= %%K in ("!dest_file_filename!") do set "dest_file_dir=!dest_file_full:%%K=!"
    

    Here, !dest_file_filename! is expanded when the entire for /F loop is parsed; the second parsing phase applies to the body of the loop and receives the value of dest_file_filename (for instance, config.php) instead of %%K.

    The advantage of this method is that immediate expansion is avoided completely, which could cause trouble in case " could occur in the value of dest_file_filename; however, this introduces problems with ! and ^ (they need to be escaped by preceding with ^).
    The value of dest_file_filename must still not begin with ~ or * and must still not contain =. It must also not begin with !.

    0 讨论(0)
  • 2020-12-22 05:51

    String manipulation in (code blocks) gets sometimes confusing. I personally prefer to put these into a sub. This batch temporarily shuts off the prompt and Echo on to have a better output of your alterations:

    @echo off
    setlocal enabledelayedexpansion
    Set MyPrompt=%Prompt%
    Prompt $S
    Echo on
    set "source=input dir"
    set "target=output dir"
    for /f "tokens=* usebackq" %%A in ("file_list.txt") do call :sub "%%A"
    pause
    Prompt %MyPrompt%
    Goto :Eof
    
    :Sub
    set "FILE=%~1"
    set "dest_file_full=%target%\!FILE:%target%=!"
    set "dest_file_filename=%~nx1"
    set "dest_file_dir=!dest_file_full:%dest_file_filename%=!"
    Rem if not exist "%dest_file_dir%" md "%!dest_file_dir%"
    set "source_file_full=%source%\%FILE%"
    @Echo copy "%source_file_full%" "%dest_file_dir%"
    

    Especially the second set is unclear to me, more real vars should help.

    0 讨论(0)
  • 2020-12-22 05:53
    FOR %%y IN ("!dest_file_full!") DO set "dest_file_dir=%%~dpy"
    

    should get your destination drive+path+terminal \ into dest_file_dir.

    needless to say, no doubt, the :: form of comment shouldn't be used in a block.

    0 讨论(0)
提交回复
热议问题