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!
)
Related
I am creating a batch file to find if any files are present in the path d:\Users\gladiator\Desktop\my docs.
If any files are present, I need to trigger an email stating which are the files present in that path.
This is the snippet of my batch script.
#echo off
for /f "delims=" %%F in ('dir /b /s "d:\Users\gladiator\Desktop\my docs"') do set MyVariable=%%F
echo %MyVariable%files are present
But this script is not displaying all the files present in that location.
Could someone help me to modify the script so as to display all the files present in the location?
Try to use setlocal enabledelayedexpansion to start a local environment before your loop, as described here:
https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/setlocal
Then, after you're done, use endlocal
When you expand the variable MyVariable use !MyVariable! which is how "delayed expansion" within a local environment works (rather than %MyVariable%).
Microsoft does not allow modification of an environment variable with set variable=... within a loop, without using "delayed expansion."
Here is an example with your code:
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%F in ('dir /b /s "d:\Users\gladiator\Desktop\my docs"') do (
set MyVariable=%%F
echo !MyVariable! file is present
)
endlocal
This will at least allow you to see the files present. Beyond that I'm not sure what you wish to do once filenames are detected.
If you want to fill MyVariable with all files found, then you can do this
#echo off
setlocal enabledelayedexpansion
set MyVariable=
for /f "delims=" %%F in ('dir /b /s "d:\Users\gladiator\Desktop\my docs"') do (
set MyVariable=!MyVariable! %%F
)
echo !MyVariable! files found
endlocal
I have a set of codes in a file, i want to rename the files in a folder with that code. Like first code for the first file then second code for the second file.
for /f "delims=" %%x in (code.csv) do call :rename "%%x"
pause
GOTO :EOF
:rename
REN "*.jpg" "a/%1.jpg"
This is what I have written but this is like running the first code for all the files and am getting a duplicate file name that exists or the file can not found error. Even though the files are being renames but it takes lots of time which I believe due to some unnecessary looping.
Any help to make it perfect will be appritiated
You need a way to read the directory and the csv in parallel. There is a trick to do that:
#ECHO OFF
setlocal enabledelayedexpansion
<code.csv (
for /f "delims=" %%x in ('dir /b *.jpg') do (
set "NewName="
set /p NewName=
if not defined NewName goto :eof
ECHO ren "%%x" "!NewName!"
)
)
NOTE: remove the ECHO command after verifying it does what you want to actually enable the ren command.
<code.csv ( ... ) provides one line after the other to the set /p within the for loop. Clear it before reading, so the NewName variable gets undefined, when there are no more names from the csv. Exit the for loop, when it is undefined (no more names).
The loop stops, when there are either no more files to be renamed or no more new names from the csv file.
See dir /? - especially the /o switch for how to sort the files (if needed) for names, dates or sizes.
Another possibility is to build an array of either the csv content or the files but I think that's an overkill for this task.
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
)
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.
Not sure what the setlocal is for, but I found it in a script example for something else that required multiple commands in a for loop.
Filelist.txt holds a list of files with the full file path to a share.
For example: \\FileShare\Division\Project\file.txt
setlocal enabledelayedexpansion
for /f "delims=" %%i in (filelist.txt) do (
mkdir "D:\Archive\%%~dpi"
move "%%i" "D:\Archive\%%i")
endlocal
The script takes the files in filelist.txt (line by line) and copies the directory structure of the path then moves the file over. Using my example above it creates the directory D:\Archive\FileShare\Division\Project\ and then moves the file file.txt to that directory.
I sort of pieced this together from existing examples of different tasks that I found on here. Any tips on cleaning it up?
Edit: To address a comment from will below. The script was created to work off a report generated of files that had not been accessed in over a year. This is why it needed to work off a list of files because I wanted to keep the folder structure and all the other files intact. If there was some way to integrate this into the script that would be awesome!
You do not need the delayed expansion or the setlocal. setlocal will isolate the scope of the section so that any changes made to variables are kept isolated to that scope. Delayed expansion is only needed if you are expanding variables within loops or if statements. There is not much to really cleanup with your script. If it accomplishes your tasks without issue and does so efficiently, then there is no need for change.
Changes and Why
setlocal EnableExtensions The scope isolation is not needed but Extensions must be enabled to use the for /f option.
%%F instead of %%i because capital letters are more easily differentiated from the modifiers. (Readability)
if exist "%%~fA" to verify that the file in the list still exists when the script is run.
for /f "tokens=1 delims=:" %%D in ("%%~dF") do because folder names cannot contain colons. This will remove the colon character from the file path for the drive.
&& Only try moving the file if the destination was crated successfully.
Code:
rem Hide the Command Output.
#echo off
rem Isolate Variable Scope and Enable Extensions.
setlocal EnableExtensions
rem Loop through the file list and verify file existence.
for /f "delims=" %%F in (filelist.txt) do if exist "%%~fF" (
rem Parse the drive letter since colon : is not a valid folder character.
for /f "tokens=1 delims=:" %%D in ("%%~dF") do (
rem Create the folder hierarchy
mkdir "D:\Archive\%%~D\%%~pF" && move "%%~fF" "D:\Archive\%%~D\%%~pF"
)
)
endlocal