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
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 !
.
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.
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.