Batch script - dynamic/variable paths in loop - windows

My requirement is to write a batch script to find and replace the a string in all of the *-spec.js files in given set of directories. Hence I have written the batch file and running the batch script as below.
<script file name> <search_string> <replace_string> <folder_path_1> <folder_path_2> <folder_path_n>
(I am sure the folder_path_n will not go beyond 7)
e.g C:\CI\Scripts>replace.bat hello world C:\app\e2e C:\sppa\e2e
So my script is as below.
#echo off
setlocal enabledelayedexpansion
set argCount=0
for %%x in (%*) do (
set /A argCount+=1
set "argVec[!argCount!]=%%~x"
)
set search=%1
set replace=%2
echo Search is %search%
echo Replace is %replace%
for /L %%i in (3,1,%argCount%) do (
set path=!argVec[%%i]!
echo path is !path!
for /R !path! %%F in (*spec.js) do (
echo %%F
)
)
Here it prints the 4 arguments correctly even the path is also printed as expected. In the next step it's expected to loop through the given path and gets all of the files that ends with "spec.js".
e.g not working:
for /R !path! %%F in (*spec.js) do (
echo %%F
)
But it prints nothing. Instead if the variable is replaced with hard coded value, it works as expected.
e.g - working:
for /R C:\app\sppa\e2e %%F in (*spec.js) do (
echo %%F
)
Your help is highly appreciated!

The reason it's not working is that the variable content must be known when the loop is parsed means that it only works with variables that can be expanded without delayed expansion. Nevertheless there is an easy solution as a for /r loop is working with the current path (from ss64).
If the [drive:]path are not specified they will default to the current drive:path.
Just change directory to !path! and then go back:
rem Change into !path! and remember directory coming from
pushd !path!
for /R %%F in (*spec.js) do (
echo %%F
)
rem Change back to origin
popd

I also tried similar as suggested by #Andre Kampling as below.
cd !path!
::echo Changed the path to !path!
FOR /R %%F IN (*spec.js) DO (
echo %%F
)

Related

How to rename files in subfolder into a specific format

I have files named as RabcdYYMMKKACCOUNT.TXT in the Subfolders of a folder where YYMM is year, month this will change. KK is another identifier, I want all the files to be renamed to MSFKKDNB.ABC, the KK is the identifier in the input file.
Below is the one i tried and the result of it:
FOR /R %%f IN (*account.txt) DO REN "%%f" *dnb.abc
R00531706AUAccount.txt is renamed to R00531706AUAccount.txtdnb.abc
but the output should be MSFAUDNB.abc
This could be done for example with:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /R %%I in (???????????account.txt) do (
set "FileName=%%~nI"
set "NewFileName=MSF!FileName:~9,2!DNB.abc"
if not exist "%%~dpI!NewFileName!" (
ren "%%~fI" "!NewFileName!" 2>nul
if not exist "%%~dpI!NewFileName!" echo Failed to rename file: "%%~fI"
) else (
echo Cannot rename file: "%%~fI"
)
)
endlocal
The file name of found account text file is assigned to environment variable FileName.
The new name for the file is created by concatenating the fixed parts MSF and DNB.abc with the 2 characters to keep from file name using string substitution and delayed expansion.
Next it is checked if a file with new name does not already exist. Is this the case the file renaming is done otherwise an error message is output.
After renaming the file it is checked if that was successful. A slightly different error is output if renaming failed for example because of a sharing violation.
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.
echo /?
endlocal /?
for /?
if /?
ren /?
set /?
setlocal /?
Read also the Microsoft article about Using Command Redirection Operators.
Try this:
#Echo Off
For %%A In ("*account.txt") Do (Set "_=%%~nA"
SetLocal EnableDelayedExpansion
Ren "%%A" "MSF!_:~-9,2!DNB.abc"
EndLocal)
I would probably do it the following way, provided that the files to rename are located in immediate sub-directories (YYMM) of the given root directory and nowhere else:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_ROOT=." & rem // (specify path to the root directory)
for /D %%D in ("%_ROOT%\????") do (
for %%F in ("%_ROOT%\%%~nxD\R??????????Account.txt") do (
set "FDIR=%%~nxD" & set "FILE=%%~nxF"
setlocal EnableDelayedExpansion
ECHO ren "!_ROOT!\!FDIR!\!FILE!" "MSF!FILE:~9,2!DNB.abc"
endlocal
)
)
endlocal
exit /B
If you want to check whether both the sub-directory name and the year/month portion of the file names are purely numeric, you could use the following script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_ROOT=." & rem // (specify path to the root directory)
for /F "delims= eol=|" %%D in ('
dir /B /A:D "%_ROOT%\????" ^| ^
findstr "^[0123456789][0123456789][0123456789][0123456789]$"
') do (
for /F "delims= eol=|" %%F in ('
dir /B /A:-D "%_ROOT%\%%~nxD\R??????????Account.txt" ^| ^
findstr "^R....[0123456789][0123456789][0123456789][0123456789].."
') do (
set "FDIR=%%~nxD" & set "FILE=%%~nxF"
setlocal EnableDelayedExpansion
ECHO ren "!_ROOT!\!FDIR!\!FILE!" "MSF!FILE:~9,2!DNB.abc"
endlocal
)
)
endlocal
exit /B
If you want to check whether the sub-directory name matches the year/month (YYMM) portion of the file names, replace the pattern R??????????Account.txt by R????%%~nxD??Account.txt (for both scripts).
After having verified the correct output of either script, remove the upper-case ECHO commands to actually rename any files!
Basically, both scripts use sub-string expansion to extract the identifier part (KK) from the file names. Since there are variables set and read in the same block of code, delayed expansion is required for that. The second approach does not list the sub-directories and files by standard for loops, it uses the dir command, findstr to filter their names and a for /F loop to capture the resulting output for both sub-directories and files.

Trouble with nested IF in cmd batch file

The batch file below intends to find all files that don't match the set pattern and delete them. However, it won't execute at all. Looks like there is syntax issue in the IF statement that I couldn't find.
SETLOCAL ENABLEDELAYEDEXPANSION
SET SHARE_FOLDER=\\blyfs01\teams$\Hadoop\Workday\
SET WKDAY_FNAME=WKDY_HADOOP_PTODATA
FOR %%F in ("%SHARE_FOLDER%*.*") DO ( SET FNAME=%%~nxF & IF !FNAME:~0,28!==!WKDAY_FNAME!_%date:~-4%%date:~4,2%%date:~7,2% ( #ECHO DO #DEL %%F) )
elzooilogico got it right. It literally is the quote that made it work!
FOR %%F in ("%SHARE_FOLDER%*.*") DO ( SET FNAME=%%~nxF & IF "!FNAME:~0,28!" NEQ "!WKDAY_FNAME!_%date:~-4%%date:~4,2%%date:~7,2%" (#ECHO #DEL %%F) )

Read directories and split path

To list the directories I use this:
set folder=C:\temp
for /d %%a in ("%folder%\*") do (
echo %%~fa
)
To splith the file path I use this:
for %%f in (%MYDIR1%) do set myfolder=%%~nxf
echo %myfolder%
Now I want put both together:
#echo off
set folder=C:\Windows
for /d %%A in ("%folder%\*") do (
for %%d in (%%~fA) do set lastfolder=%%~nxf
echo %lastfolder%
)
All I get in thes result is %~nxf. I tried some things, but I didn't get a correct result. What I'm doing wrong?
What I don't understand in these examples is %~fA and %~nxf. Don't know where you can look up things like this.
Edit:
%~nxf to get file names with extensions
where F is the variable and ~n is the request for its name | Source
%~fI Expands %I to a fully qualified path name.
Now I modified my code with the new information:
#echo off
for /d %%A in ("%folder%\*") do (
for %%D in (%%~fA) do (
set lastfolder=%%~nxD
echo %lastfolder%
)
)
Now I get as result the last folder, but this is printed as many times as subfolders are existing. So I only get the last one. How can I iterate over each?
Solution:
Thanks to bgalea this is my solution:
#echo off
setlocal enabledelayedexpansion
set folder=C:\Windows
for /d %%A in ("%folder%\*") do (
for %%D in (%%~fA) do (
set lastfolder=%%~nxD
echo !lastfolder!
)
)
endlocal
Things in bracket are one line. Therefore you have to use !var! which you turn on with setlocal enabledelayedexpansion. See set /? and setlocal /?.
. is current directory and .. is parent directory.
So c:\temp\.. is the same as c:\
%~nx1 etc are documented in the call command's help - call /?
My answer here Trouble with renaming folders and sub folders using Batch has a list of command prompt punctuation.

How to set a variable with an environment variable read from a text file

I've created the following config file which contains parameters to be used by a batch file:
File winscp.conf:
folder %appData%\winscp
version 5.7.4
visit http://sourceforge.net/projects/winscp/files/WinSCP/
download http://sourceforge.net/projects/winscp/files/WinSCP/5.7.4/winscp574.zip
Batch file (get.bat):
#echo off
setlocal
#if not exist "%1" (
echo Config file not found in "%1"
exit /B
)
#for /f "tokens=1,2 delims= " %%A in (%1) do (
set %%A=%%B
)
mkdir %folder%
When I call the batch file like this:
get.bat winscp.conf
I get a sub-folder %appData%\winscp created in the current folder, something like this:
c:\Temp\%appData%\winscp
While what I want is a winscp folder created in the Windows app data folder, something like this:
C:\Users\Caffe\AppData\Roaming\winscp
I think there's something wrong with the statement set %%A=%%B, since if I change it to set %%A=%appData%\winscp I do get the folder created the way I want.
I just summarize the answers of wOxxOm and JosefZ with adding some small improvements.
#echo off
setlocal EnableExtensions
set "ConfigFile=%~1"
if "%ConfigFile%" == "" (
echo %~nx0 must be called with name of configuration file
echo as first parameter, for example: %~nx0 winscp.conf
goto EndBatch
)
if not exist "%ConfigFile%" (
echo Config file "%ConfigFile%" not found.
goto EndBatch
)
set "folder="
for /F "usebackq tokens=1*" %%A in ("%ConfigFile%") do call set "%%A=%%B"
if "%folder%" == "" (
echo Option "folder" not defined in config file "%ConfigFile%".
goto EndBatch
)
mkdir "%folder%" 2>nul
:EndBatch
endlocal
For better reading what first argument passed to the batch file should be, the parameter is immediately assigned to an environment variable without the surrounding quotes which must be used if configuration file name with or without path contains anywhere at least 1 space character.
Next a check is made if batch file was called at all with a parameter.
After FOR loop it is verified if the configuration file really contained an entry for folder.
And creation of directory is made with using quotes because folder path can contain spaces, and with redirecting any error message to device nul, i.e. suppress the error message output if directory exists already.
The variables inside tokens should be expanded prior to the assignment by adding call:
call set %%A=%%B
With next changes in your code:
call set instead of set
for /f "tokens=1,* instead of for /f "tokens=1,2
then next code snippet should work:
#ECHO OFF
SETLOCAL enableextensions
if not exist "%1" (
echo Config file not found in "%1"
exit /B
)
for /f "tokens=1,* delims= " %%A in (%1) do (
call set "%%A=%%B"
)
mkdir %folder%

Windows batch file error passing variable (.bat)

I'm trying to loop recursively through all the files inside a folder.
I need to read the file name, abcd_somethingone_pqrs.csv is the file name pattern. I need to create a folder (if the folder does not exist) named somethingone and move the file into that folder. I have started writing the following code in a .bat.
FOR /R D:\MOE\MRs\batchfiles\01\ %%F in (*.*) do (
echo %%~nF
set newloc=%%~nF
echo %newloc%
)
The line1 prints the filenames in the folder correctly. But when I read it to the new variable newloc, always the line4 prints only the last file's name. Can anyone please realize what's going wrong here or propose me a method to do this.
EDIT: Totally changed my answer and actually attempted to completely solve your overall problem instead of just the thing you were stuck on.
You'll have to update target_dir to wherever you want the files to go.
#echo off
setlocal enabledelayedexpansion
set source_dir=D:\MOE\MRs\batchfiles\01\
set target_dir=D:\something\
:: Recursively search the source directory for files
for %%A in (somethingone somethingtwo somethingthree) do (
for /F "delims=" %%B in ('dir /a:-d /s /b %source_dir%*%%A*') do (
REM if the directory does not exist, make it
if not exist %target_dir%%%A mkdir %target_dir%%%A
move %%B %target_dir%%%A
)
)
You need to enable delayed expansion so that the variable value inside the for loop will propagate correctly.
setlocal enabledelayedexpansion
for /R D:\MOE\MRs\batchfiles\01\ %%F in (*.*) do (
echo %%~nF
set newloc=%%~nF
echo !newloc!
)

Resources