Splitting file path in batch using for loop and variable substitution - windows

I have searched around for quite a while without any luck in getting my script working. I feel like I'm pretty close, but need a little help. I am attempting to use a FOR loop to recursively scan "srcdir" (set at the beginning of my script), then once the loop returns files/paths (%%f), then I can substitute part of the file path with something else (eg; C:\rootpath\src for C:\rootpath\des).
I am able to do something just like this by using a script like this one:
set subdir=C:\rootpath\src
set subdir=%subdir:src=des%
echo %subdir%
However, what makes this difficult is that the root path of my "srcdir" may change (eg; C:\roothpath) and everything recursively after the "srcdir" may change (eg. anything after folder "src" in C:\rootpath\src). The only constant paths the folder src and the folder des (located in the same directory where I am running my batch file from).
So, by using the same technique in the previous example, I want to use a FOR loop to recursively find the full path of the files in "srcdir" (%%f) and substitute the folder "src" with the folder "des" in the path string. Therefore, I am trying to set "%%f" as a variable (subdir) and replace the folders using variable substitution.
Here is my current non-working script:
set srcdir=C:\rootpath\src
for /r "%srcdir%" %%f in (*.txt) do (
set subdir=%%f
set subdir=%subdir:src=des%
echo %subdir%
)
Any help would be greatly appreciated! Thanks!

You need to enable delayed expansion since you are assigning and reading variables within a block of code like a for loop:
setlocal EnableDelayedExpansion
set "srcdir=C:\rootpath\src"
for /R "%srcdir%" %%F in ("*.txt") do (
set "subdir=%%~fF"
set "subdir=!subdir:\src\=\des\!"
echo(!subdir!
)
endlocal
The setlocal EnableDelayedExpansion command enables delayed expansion; it also localises the environment, meaning that changes to environment variables are available only before endlocal is executed or the batch file is terminated.
To actually use delayed expansion, you need to replace the percent signs by exclamation marks, so %subdir% becomes !subdir!.

Related

Windows Batch Script - Read file names into variables

I am trying to convert files (movies) in a directory, one by one. Each new, converted file needs a new file name assigned - the old one with a new extension.
I've tried the below to read the source file, strip off the extension and assign a new one:
#echo off
setlocal DisableDelayedExpansion
cd \target_dir
for %%x in (*) do (
echo %%x
set source=%%x
echo Source #%source%#
set target=%source:.mpg=%
echo Target #%target%#
transcode %source% %target%.mp4
)
Unfortunately, this is not working. As the output shows, I am not even managing to copy the current file into the variable "source":
E:\target_dir>..\test.bat
movie1.mpg
source ##
target ##
movie2.mpg
source ##
target ##
I googled around and thought I'd have found the right syntax, but that doesn't appear to be it. Thanks for any help!
This works:
#echo off
cd \target_dir
for %%x in (*) do (
transcode "%%x" "%%~nx.mp4"
)
Note that you cannot use the %%~n syntax on standard environment variables - you need to directly access the for loop variable.
Edit: Added quotes to make the batch work with files with spaces in their names as well.
jlahd shows a correct solution without copying the filename to a variable.
But I want to explain your current problem.
As the output shows, I am not even managing to copy the current file
into the variable "source":
You found the BBB (batch beginner bug), in reality you set the variable but you failed to access the content later.
This is an effect of the batch parser working with code blocks, as these blocks are parsed and percent expansion will be done before the code will be executed.
A code block is the code inside of parenthesis or commands concatenated by &, && or ||.
To avoid this problem the delayed expansion was introduced.
Then you simply can use !variable! to expand a variable at runtime.
setlocal EnableDelayedExpansion
cd \target_dir
for %%x in (*) do (
echo %%x
set source=%%x
echo Source #!source!#
set target=!source:.mpg=!
echo Target #!target!#
transcode !source! !target!.mp4
)

Windows Batch setting PATH through loop

I'm trying to create a drive with a suite of programs that can be implemented through the command prompt.
My issue is setting the PATH environment variable to include paths to the program suite. Since there are a couple directories for these suites (and I'm constantly adding more), I am trying to get it so I can have a .ini file be parsed through a simple Batch script and returned. From this I want to take the path parsed from the .ini file and append it to my current PATH variable.
This is the current script I'm trying to get fixed.
FOR /F "delims=" %%i IN ('call %cd:~0,3%\initTermPort\parse.ini.bat %cd:~0,3%\initTermPort\config.ini Alternate _path') DO (
SET PATH="%PATH%;%cd:~0,3%%%i"
)
The call to parse.ini.bat is getting the next path in config.ini on every run.
For example:
If I substitute SET PATH="%PATH%;%cd:~0,3%%%i" with ECHO %%i I get the following output:
E:\> (ECHO ~\bin)
~\bin
E:\>(ECHO ~\library\bin)
~\library\bin
But for some reason SET PATH="%PATH%;%cd:~0,3%%%i" is not setting the PATH at all.
Any help is appreciated.
The problem is that when the for code block (the code enclosed in parenthesis) was parsed, all the read operations to variables were replaced with the value in the variable before starting to execute, and in each iteration what is used is this initial value, and not the value inside the variable during the execution.
If you change a variable inside a block of code and need to access the changed value inside the same block of code, you need to enable delayed expansion and change the syntax to access the variables to use !varName! instead of %varName%. This indicates to the parser that this read operation must be delayed.
setlocal enableextensions enabledelayedexpansion
....
FOR /F "delims=" %%i IN (
'call %cd:~0,3%\initTermPort\parse.ini.bat %cd:~0,3%\initTermPort\config.ini Alternate _path'
) DO (
SET "PATH=!PATH!;%cd:~0,3%%%i"
)

Batch rename files using find in content

Long-time listener first-time caller.
I'm trying to use a windows batch script to search for specific strings in files contained in a directory, and classify them with a file name and random value for further processing.
This is what I have put together based on similar posts:
setlocal enabledelayedexpansion
for %%a in (C:\Temp\*.txt) do (
set t1=string1-%RANDOM%.txt
set t2=string2-%RANDOM%.txt
find "string1" %%~fa && ren %%~fa %t1%
find "string2" %%~fa && ren %%~fa %t2%
)
If I move the set commands outside the for loop it will work, but I need it to generate a random file name within the loop in case there's multiple files found with 'string1'.
Any help much appreciated!
Thanks.
In cmd, variables are replaced with it value when the block in which they are placed is readed. In your case, your set variables are replaced once, when the for is readed, and changes are not visible because once that replacement is done, there is no more variable replacement. That is the reason for the enabledelayedexpansion, but a change in code is needed
setlocal enabledelayedexpansion
for %%a in (C:\Temp\*.txt) do (
set t1=string1-!RANDOM!.txt
set t2=string2-!RANDOM!.txt
find "string1" "%%~fa" && ren "%%~fa" !t1!
find "string2" "%%~fa" && ren "%%~fa" !t2!
)
For delayed expansion of variables, it is necessary to indicate to cmd which of the variables should be replaced in each access. To do it, change % with ! when using the variable. This is not necessary for control variables in for command (your %%a), for which it is assumed.
Be careful, your code does not handle the posibility of duplicate file names. Random means random, not unique. Collisions are possible.

batch script variable unset in for loop has no effect

Below is my script. I am trying to look into folders one level below and pick out only those folders, hence the ~-9 which extracts the last 9 chars from the path. But the set var= does not unset the variable because the output comes back with the same folder name repeated # times. Also batch doesn't allow me to do this extract trick directly on %%i, hence the need for the local variable.
How do I clear this variable so that it takes the new value in the next iteration?
#echo off
for /d %%i in (%1\*) do (
set var=%%i
echo %var:~-9%
set "var="
)
http://judago.webs.com/variablecatches.htm has an explanation for my problem. The magic lines were setlocal enabledelayedexpansion and calling var as echo !var:~-9!. ! vs % ...wow! cmd still amazes me.
You found the source of your problem, as well as the solution - delayed expansion.
But using FOR while delayed expansion is enabled can cause problems if any of the filenames contain the ! character. The expansion of the for variable %%i will be corrupted if the value contains ! and delayed expansion is enabled. This is not a frequent problem, but it happens.
The solution is to toggle delayed expansion on and off within the loop
#echo off
setlocal disableDelayedExpansion
for /d %%i in (%1\*) do (
set var=%%i
setlocal enableDelayedExpansion
echo !var:~-9!
endlocal
)
I'm also wondering what you mean by "I am trying to look into folders one level below and pick out only those folders, hence the ~-9 which extracts the last 9 chars from the path". I suspect your are trying to get the name of the child folder, without the leading path information. If that is so, then using the substring operation is not a good solution because the length of folder names varies.
There is a very simple method to get the name of the folder without the leading path info:
for /d %%i in (%1\*) do echo %%~nxi

What does this batch file code do?

What does this bat code do?
for /f %%i in ('dir /b Client\Javascript\*_min.js') do (
set n=%%~ni
set t=!n:~0,-4!
cp Client\Javascript\%%i build\Client\Javascript\!t!.js
)
What does %%~ni,~n:~0,-4!,%%i,!t! mean?
Keep in mind that in batch files, you need to escape percentage signs unless you're referring to arguments given to the batch file. Once you remove those, you get
for /f %i in ('dir /b Client\Javascript\*_min.js') do (
set n=%~ni
set t=!n:~0,-4!
cp Client\Javascript\%i build\Client\Javascript\!t!.js
)
%i is the declaration of a variable used to place the current file for has found. %~ni extracts the filename portion of %i. !n:~0,-4! uses delayed expansion to remove the last four characters from %n% (set in the previous line) !t! is simply delayed expansion of the %t% variable set in the previous line.
Delayed expansion is used because otherwise, the variables will be substituted as soon as the line is encountered, and future iterations will not re-expand the variable.
for /f %%i in ('dir /b Client\Javascript\*_min.js') do (
Iterate over every file in the Client\Javascript folder that match "*_min.js". Thedircommand andfor /f` are totally unneeded here, though and only complicate things, especially when file names contain spaces, commas and the like. A more robust and simpler alternative would be
for %%i in (Client\Javascript\*_min.js) do (
But that's just beside the point. People tend to write unelegant batch files sometimes, ignoring the pitfalls and common errors. That's just one example of that.
set n=%%~ni
Creates a variable n, containing the file name (without any directory information or extension) of the file currently processed. We remember that the for statement iterates over every file it finds. With this line starts what it does with those files.
set t=!n:~0,-4!
Creates a second variable, t, containing everything but the last four characters of the file name. This essentially strips away the "_min"
cp Client\Javascript\%%i build\Client\Javascript\!t!.js
Finally, this copies the original file to the directory build\Client\Javascript with the new name, just constructed. So a file like Client\Javascript\foo_min.js will be copied to Client\Javascript\foo.js. The !t! here is just a delayed-evaluated environment variable. More on that below. Here it should suffice that it just inserts the contents of said variable at that point in the line.
Again, bad practice here that will break in numerous interesting ways:
cp is not a command on Windows so this batch will assume cygwin, GNUWin32 or similar things installed. I tend to avoid having too many unneeded dependencies and stick to what Windows provides; in this case the copy command. Two bytes won't kill anyone here, I think.
No quotes are around either argument. Leads to interesting results when spaces start appearing in the file name. Not good, either.
As for why delayed expansion was used (! instead of % surrounding the variables: The for command consists of everything in the block delimited by parentheses here as well. The entire block is parsed at once and normal variable expansion takes place when a line/command is parsed. That would mean that every variable in the block would be evaluated before the loop even runs, leaving just the following:
for /f %%i in ('dir /b Client\Javascript\*_min.js') do (
set n=%%~ni
set t=
cp Client\Javascript\%%i build\Client\Javascript\.js
)
which is certainly not what you want in this case.
Delayed expansion is always needed when creating and using variables in a loop such as this. A workaround not needing delayed expansion would be to offload the loop interior into a subroutine:
for /f %%i in ('dir /b Client\Javascript\*_min.js') do call :process "%%i"
goto :eof
:process
set n=%~n1
set t=%n:0,-4%
copy "Client\Javascript\%~1" "build\Client\Javascript\%t%.js"
goto :eof
Since the subroutine is not a single "block" (something delimited by parentheses) it will be parsed line by line as usual. Therefore it's safe to use normal expansion instead of delayed expansion here.
A complete help for the FOR command can be found on the Microsoft TechNet site. See here for more information on delayed expansion :
// Pseudo code
for each file named *_min.js in the specified directory
n is set to the file name (*_min)
t is set to the file name, excluding the last 4 characters (*)
the file is copied and renamed t.js to the specified directory
%~ni expands to just the filename part of i.
!n:~0,-4! expands to all but the last four characters of n.
In general, help for at the command prompt will give an overview of the multitude of ways for can expand variables these days.

Resources