batch rename file while keeping part rar number - batch-file

Like the title suggests I want to batch rename files but keep the part of rar file.
Example:
File-01.part1.rar
File-01.part2.rar
Output:
Newfile__01.part1.rar
Newfile__01.part2.rar
Below is the rename part of the code.
SETLOCAL EnableDelayedExpansion
REM Set your Year, Month, Day variable values here.
REM They will be used for file renaming.
CD "D:\test\rename"
FOR /F "usebackq tokens=* delims=" %%A IN (`DIR "*.rar" /B /A:-D`) DO (
REM Extract the last 2 chars of the file name.
SET FileName=%%~nA
SET First4=!FileName:~0,5!
SET Last2=!FileName:~-2!
REM Rename the file, inserting the new data.
RENAME "%%A" "!First4!__!Last2!%%~xA"
)
ENDLOCAL

Here's an example script based on my assumption of what you wanted to do:
#Echo Off
SetLocal EnableDelayedExpansion
Set "sd=D:\test\rename"
Set "xf=.rar"
If Not Exist "%sd%\*%xf%" Exit /B
CD /D "%sd%" 2>Nul || Exit /B
Set "ds="
For /F "Tokens=1-3 Delims=/ " %%A In ('RoboCopy/NJH /L "\|" Null'
) Do If Not Defined ds Set "ds=%%A%%B%%C"
For %%A In ("*%xf%") Do (Set "fn=%%~nA"
For %%B In ("!fn!") Do (Set "fx=%%~xB"
If /I Not "!fx:.part=!"=="%%~xB" Set "fn=%%~nB")
Ren "%%A" "!fn:~,4!_%ds%_!fn:~-2!!fx!%xf%$")
Ren "*%xf%$" "*%xf%"
I have slotted the year, month, day variable, %ds% in between the two underscores, meaning:
File-01.part1.rar would be renamed to File_20180523_01.part1.rar
Testing18.part2.rar would be renamed to Test_20180523_18.part2.rar
Archive02.rar would be renamed to Arch_20180523_02.rar.

Try this batch file after modification of folder path in third line:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Folder=D:\test\rename"
for /F "eol=| delims=" %%A in ('dir "%Folder%\File-01.part*.rar" /A-D-H /B /ON 2^>nul') do (
for /F "eol=| delims=" %%B in ("%%~nA") do ECHO ren "%Folder%\%%A" "Newfile__01%%~xB%%~xA"
)
endlocal
pause
Note: The command ECHO is used here on line 6 to just demonstrate what would be the rename command line. Run the batch file as is for verification. When everything looks okay, remove ECHO and run the batch file once again to really rename the files.
This batch file runs FOR which runs the command DIR with using a separate command process started in background with cmd.exe /C to output
just the names in bare format because of /B
of only non hidden files because of /A-D-H (attribute not directory and not hidden)
sorted by name because of /ON
matching in specified folder the wildcard pattern File-01.part*.rar.
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background. 2>nul redirects the error message output on no file matching the criteria from handle STDERR to device NUL to suppress it.
FOR captures the output of started command process and processes it line by line with ignoring empty lines.
The default end of line character ; is modified with eol=| from semicolon to vertical bar. FOR ignores by default lines starting with a semicolon. A file name can start with a semicolon. But a file name can't contain a vertical bar.
FOR splits up by default a line on spaces/tabs and assigns just first part to specified loop variable A. The line splitting behavior is disabled by delims= which specifies an empty list of delimiters resulting in getting the file name with file extension but without file path as output by DIR assigned to the loop variable.
The inner FOR loop processes as string just the file name without file extension. This is the string from first character up to character before last dot, i.e. File-01.part1 and File-01.part2 and assigns this part to loop variable B. For the inner FOR the file extension is again everything after last dot which is .part*. The real file extension .rar of the file is unknown for the inner FOR loop processing just a file name string.
The command REN renames the current file referenced full path to new file name with keeping .part* referenced with %%~B from inner FOR loop and original file extension .rar from outer FOR loop.
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.
dir /?
echo /?
endlocal /?
for /?
pause /?
ren /?
setlocal /?

Related

Filename editing with batch

So I'm working on a script for file renaming/editing and I'm getting an error
The system cannot find the file specified
I'm assuming it's because of the loop, how can I fix this
#echo off
Setlocal enabledelayedexpansion
cd /d "%~dp0"
for /f "delims=*" %%a IN ('dir /b /s /a-d "Folder1\(*)*.txt"') DO (
Set "File=%%~nxa"
Ren "%%a" "!File:(1)=(-125)!"
Ren "%%a" "!File:(2)=(-124)!"
Ren "%%a" "!File:(3)=(-121)!"
Ren "%%a" "!File:(4)=(-117)!"
Ren "%%a" "!File:(5)=(-120)!"
Ren "%%a" "!File:(6)=(-116)!"
Ren "%%a" "!File:(7)=(-115)!"
Ren "%%a" "!File:(8)=(-127)!"
Ren "%%a" "!File:(9)=(-126)!"
Ren "%%a" "!File:(10)=(-100)!"
)
Source Folder1
(1) filename.txt
(2) filename.txt
(3) filename.txt
Source Folder1 Results
(-125) filename.txt
(-124) filename.txt
(-121) filename.txt
As you can see the script does work, but I am getting the message
I also tested it with this for /f "Tokens=*" same results
There can be used the following code for this task.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for /F "delims=" %%I in ('dir "(*)*.txt" /A-D-L /B 2^>nul') do (
set "FileName=%%I"
setlocal EnableDelayedExpansion
set "NewName=!FileName:(1)=(-125)!"
set "NewName=!NewName:(2)=(-124)!"
set "NewName=!NewName:(3)=(-121)!"
set "NewName=!NewName:(4)=(-117)!"
set "NewName=!NewName:(5)=(-120)!"
set "NewName=!NewName:(6)=(-116)!"
set "NewName=!NewName:(7)=(-115)!"
set "NewName=!NewName:(8)=(-127)!"
set "NewName=!NewName:(9)=(-126)!"
set "NewName=!NewName:(10)=(-100)!"
ren "!FileName!" "!NewName!"
endlocal
)
popd
endlocal
There is first defined the required execution environment with
command echo mode turned off and
command extensions enabled and
delayed variable expansion disabled.
Next the subdirectory Folder1 of the batch file directory is made the current working directory or the batch file processing is exited if that folder does not exist at all.
Then one more Windows command process is started in background with %ComSpec% /c and the command line within ' of command FOR appended as additional arguments. There is executed with Windows installed into C:\Windows:
C:\Windows\System32\cmd.exe /c dir "(*)*.txt" /A-D-L /B 2>nul
The command DIR searches
in the current directory
for just files because of /A-D-L (attribute not directory and not link (reparse point))
with a file name matched by the wildcard pattern (*)*.txt and
outputs just the matching names without path in bare format because of /B.
It is possible that DIR does not find a matching file name in which case an error message is output which is suppressed by redirecting it from STDERR (standard error) to device NUL.
Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR with option /F captures all output to STDOUT (standard output) of started cmd.exe and waits for self-termination of started cmd.exe before the captured output is processed line by line.
Empty lines are ignored which do not exist here at all. There would be split up by default the file names into substrings using normal space and horizontal tab as string delimiters, analyzed if the first substring begins with a semicolon in which case the line (file name) would be also ignored while otherwise the first space/tab delimited substring is assigned to the specified loop variable I before running the commands in body of FOR.
The line (file name) splitting behavior is not wanted which is the reason for usage of the option delims= to define an empty list of string delimiters to turn off the line splitting into substrings. The default end of line character ; can be kept in this case as all file names to process start definitely with character (.
The file name without path is first assigned to the environment variable FileName which works also for file names containing one or more exclamation marks as delayed variable expansion is disabled at execution of this command line.
Next is enabled delayed variable expansion as required for the next commands. Please read this answer for details about the commands SETLOCAL and ENDLOCAL and what really happens in memory of running cmd process on using these two commands.
A series of string substitutions is done next with command SET to define the new file name based on the current file name before running just once the command REN to rename the current file to the new name.
The code posted in the question tries to rename the file multiple times. It fails on string substitution does not change anything at all because of a file cannot be renamed on new name being equal the current name. Then one REN command works on which the string substitution was successful. The other REN command fail again because of the file is already renamed and renaming it once again with original file name cannot work anymore for that reason.
The initial environment with disabled delayed variable expansion is restored last before processing the next file name.
There could be used for this task also:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for %%# in ("1 125" "2 124" "3 121" "4 117" "5 120" "6 116" "7 115" "8 127" "9 126" "10 100") do for /F "tokens=1,2" %%G in (%%#) do for %%I in ("(%%G)*.txt") do for /F "tokens=1* delims=)" %%J in ("%%I") do ren "%%I" "(-%%H)%%K"
popd
endlocal
For each pair of current and new number a string splitting is done to assign the current number to the loop variable G and the new number to the loop variable H before a FOR is used to process all files with current number (G) of which file name is assigned to loop variable I which is split up on first occurrence of a closing round bracket to get the part after first ) from entire file name with file extension assigned to the loop variable K to be able to rename the file with the new number.
There are several other solutions possible too.
To understand the commands used and how they work, open a command prompt window, execute there the following commands, and read the displayed help pages for each command, entirely and carefully.
call /? ... explains %~dp0 ... drive and path of argument 0 which is the batch file path always ending with a backslash.
dir /?
echo /?
endlocal /?
exit /?
for /?
popd /?
pushd /?
ren /?
set /?
setlocal /?
Read also single line with multiple commands using Windows batch file for an explanation of conditional command operator || used on third command line.
The first batch file solution with processing also files in subfolders:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for /F "delims=" %%I in ('dir "(*)*.txt" /A-D-L /B /S 2^>nul') do (
set "FullName=%%I"
set "FileName=%%~nI"
setlocal EnableDelayedExpansion
set "NewName=!FileName:(1)=(-125)!"
set "NewName=!NewName:(2)=(-124)!"
set "NewName=!NewName:(3)=(-121)!"
set "NewName=!NewName:(4)=(-117)!"
set "NewName=!NewName:(5)=(-120)!"
set "NewName=!NewName:(6)=(-116)!"
set "NewName=!NewName:(7)=(-115)!"
set "NewName=!NewName:(8)=(-127)!"
set "NewName=!NewName:(9)=(-126)!"
set "NewName=!NewName:(10)=(-100)!"
ren "!FullName!" "!NewName!%%~xI"
endlocal
)
popd
endlocal
The second batch file solution with processing also files in subfolders:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
pushd "%~dp0Folder1" || exit /B
for %%# in ("1 125" "2 124" "3 121" "4 117" "5 120" "6 116" "7 115" "8 127" "9 126" "10 100") do for /F "tokens=1,2" %%G in (%%#) do for /R %%I in ("(%%G)*.txt") do for /F "tokens=1* delims=)" %%J in ("%%~nxI") do ren "%%I" "(-%%H)%%K"
popd
endlocal
The text files with hidden attribute set are ignored by the FOR loop searching recursively in the subdirectory Folder1 of the batch file directory for the files with the number in round brackets assigned to the loop variable G.

get two recent files from a folder in a batch script

I am trying to write a batch script that gets two recent files based on creation time from a given directory. In this batch script i want to invoke an .exe file that takes these two files as arguments.
Can someone please help me with this.
dir /a:-d-s /b /o:-d /t:c
This is the command i used that would list the names of the files in descending order based on the creation time. The command prints the result to the console but i want to assign it to some variable or array and access the top two filenames.
The following example is intended to set the two topmost returned files as 'array type' variables %Newest[1]% and %Newest[2]%
#For /F "Delims==" %%A In ('Set Newest[ 2^>NUL')Do #Set "%%A="
#For /F "Tokens=1*Delims=:" %%A In ('Dir /B/A-D-S/O-D/TC 2^>NUL^|FindStr /N "^"'
)Do #If %%A LEq 2 (Set "Newest[%%A]=%%B")Else GoTo Next
:Next
#Set Newest[ 2>NUL&&Pause
The last line was included just to show you any variables created from the loop. You would obviously replace this line with your own code, or leave it as is, and place your code beneath it. If you want to use the most recently modified date and time stamps instead of created, replace /TC with /TW on line 2
Off-topic:
Here's an example of it in use, based upon your later posted question, since deleted:
#Echo Off
For /F "Delims==" %%A In ('Set Newest 2^>NUL')Do Set "%%A="
For /F "Tokens=1-2*Delims=:" %%A In ('Dir /B/A-D-S/O-D/TC "Checkpoints" 2^>NUL^
^|FindStr /N "^"')Do If %%A LEq 2 (Set "Newest%%A=%%B")Else GoTo Next
:Next
Set "DT1="&For /F "Tokens=1*Delims=_" %%A In ('Set Newest 2^>NUL'
)Do If Not Defined DT1 (Set "DT1=%%~nB")Else Set "DT2=%%~nB"
If Not Defined DT1 Exit /B
Set "Target=Differences\Difference_%DT1%_%DT2%.csv"
Set Newest 2>NUL
Set Target 2>NUL
If Not Exist "Differences\" MD "Differences"
Pause
Here is a commented batch file for this task.
#echo off
rem Set up a local environment for this batch file with enabled command
rem extensions as required for command FOR and with disabled delayed
rem environment variable expansion to process also correct file names
rem with one or more exclamation marks in name.
setlocal EnableExtensions DisableDelayedExpansion
rem Make directory of the batch file the current directory.
rem It is possible to use any other directory on replacing
rem %~dp0 by a directory path.
pushd "%~dp0"
rem Make sure the environment variable File1 is not defined.
set "File1="
rem Get a list of files without system attribute in current directory
rem ordered by creation date with newest first and oldest last and process
rem this list of file names line by line with ignoring the batch file. The
rem loop processing the file names is exited after having assigned second
rem newest created file in this directory to the environment variable File2
rem in addition to newest file assigned to environment variable File1 with
rem a jump to the command line below the line with label RunProgram.
for /F "eol=| delims=" %%I in ('dir /A-D-S /B /O-D /TC 2^>nul') do (
if not "%%I" == "%~nx0" (
if not defined File1 (
set "File1=%%I"
) else (
set "File2=%%I"
goto RunProgram
)
)
)
rem There is no file or just one file found in current directory other
rem than this batch file and so the program cannot be executed at all.
goto EndBatch
rem Run the program with the file names of the two newest
rem created files in current directory (batch file directory).
:RunProgram
"C:\Path To Application\Application.exe" "%File1%" "%File2%"
rem Restore the initial current directory and also the initial environment
rem variables list on starting of this batch file as well as initial state
rem of command extensions and delayed environment variable expansion.
:EndBatch
popd
endlocal
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.
call /? ... explains %~dp0 (drive and path of batch file) and %~nx0 (name and extension of batch file).
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
popd /?
pushd /?
rem /?
set /?
setlocal /?
Read the Microsoft article about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background with %ComSpec% /c and the specified command line appended.

Can I use FOR /L counter variable in the FOR command?

::for /l %%n in (0, 1, 6) do (
for /F "skip=1 delims=" %%i in (path.txt) do set "dirvar=%%i"&goto nextline
:nextline
for /F "skip=1 delims=" %%i in (file.txt) do set "filevar=%%i"&goto nextline
:nextline
for /F "skip=1 delims=" %%i in (dotonefile.txt) do set "dotvar=%%i"&goto nextline
:nextline
SET dirvar=%dirvar%
SET filevar=%filevar%
SET dotvar=%dotvar%
SET dirfile=%dirvar%%filevar%
SET dirdotfile=%dirvar%%dotvar%
IF EXIST %dirfile% (
del %dirdotfile%
) ELSE (
rename %dirdotfile% %dirfile%
)
::)
My batch script above works fine in that it runs one time. It reads the 2nd line from three separate text files into variables. Then it tests to see if a filename is in a directory and if it is named IMG001.jpg it deletes IMG001.1.jpg. in the same directory. If IMG001.jpg is NOT found in the directory, it renames IMG001.1.jpg in the directory to IMG001.jpg.
path.txt is just a text file with a list of folder paths like:
F:\My Pictures\2005-Misc\
F:\My Pictures\2006-Misc\
F:\My Pictures\2007-Misc\
file.txt is just a text file with a list file names, where line 1 is a file in the directory that's also line 1 of the path.txt file. So there could be a IMG001.jpg in the 2005-Misc folder, could be a IMG001.jpg in the 2006-Misc folder, and there could be a IMG001.jpg in the 2007-Misc folder:
IMG001.JPG
IMG001.JPG
IMG001.JPG
Similarly with dotonefile.txt, it's a list of filenames that ARE in the corresponding directory listed in path.txt. So there IS a IMG001.1.jpg in folder 2005-Misc, there's one in 2006-Misc, and there's one in 2007-Misc.
IMG001.1.JPG
IMG001.1.JPG
IMG001.1.JPG
I want to loop this script and repeat it so it reads in lines 1 through n (n can be hard coded, above it is currently 7) from the text files to variables, then tests and renames for each filename.
I tried uncommenting the first and last lines and then in the three for loops, I replaced the hardcoded "1" with "%%n" but the batch file won't run erroring with "the sntax of the command is incorrect". Below is my attempt that doesn't work. Any advice on how to tweak it to run? I've tried all kinds of combinations of making a new count variable that increments by 1 at the end, using delayed expansion in various forms of variables, nothing works.
for /l %%n in (0, 1, 6) do (
for /F "skip=%%n delims=" %%i in (path.txt) do set "dirvar=%%i"&goto nextline
:nextline
for /F "skip=%%n delims=" %%i in (file.txt) do set "filevar=%%i"&goto nextline
:nextline
for /F "skip=%%n delims=" %%i in (dotonefile.txt) do set "dotvar=%%i"&goto nextline
:nextline
SET dirvar=%dirvar%
SET filevar=%filevar%
SET dotvar=%dotvar%
SET dirfile=%dirvar%%filevar%
SET dirdotfile=%dirvar%%dotvar%
IF EXIST %dirfile% (
del %dirdotfile%
) ELSE (
rename %dirdotfile% %dirfile%
)
)
The main problem is that Windows command processor cmd.exe does not support labels inside command blocks which are parsed completely before executing the command making use of the command block. Please read for details How does the Windows Command Interpreter (CMD.EXE) parse scripts?
The solutions is using a subroutine.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /L %%N in (0,1,6) do call :ProcessFiles %%N
endlocal
goto :EOF
:ProcessFiles
if not %1 == 0 ( set "SkipOption=skip=%1 " ) else ( set "SkipOption=" )
set "DirVar="
for /F "%SkipOption%eol=| delims=" %%I in (path.txt) do set "DirVar=%%I" & goto GetFileVar
:GetFileVar
set "FileVar="
for /F "%SkipOption%eol=| delims=" %%I in (file.txt) do set "FileVar=%%I" & goto GetDotVar
:GetDotVar
set "DotVar="
for /F "%SkipOption%eol=| delims=" %%I in (dotonefile.txt) do set "DotVar=%%I" & goto CheckFile
:CheckFile
set "DirFile=%DirVar%%FileVar%"
set "DirDotFile=%DirVar%%DotVar%"
if exist "%DirFile%" (
del "%DirDotFile%"
) else (
rename "%DirDotFile%" "%DirFile%"
)
goto :EOF
A smarter approach would be using this batch file code without usage of text files at all.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir "F:\My Pictures\*.1.JPG" /A-D /B /S 2^>nul') do (
for %%J in ("%%~dpnI") do (
if exist "%%~dpnJ%%~xI" (
del "%%I"
) else (
ren "%%I" "%%~nJ%%~xI"
)
)
)
endlocal
The FOR loop starts with %ComSpec% /C one more cmd.exe command process in background to execute the command line:
dir "F:\My Pictures\*.1.JPG" /A-D /B /S 2>nul
DIR searches with the specified options for
files because of option /A-D (attribute not directory)
matching case-insensitive the pattern *.1.JPG
in directory F:\My Pictures and all its subdirectories because of option /S
and outputs in bare format because of option /B just
file name with file extension and with full path because of option /S.
DIR would output an error message in case of no file can be found in entire directory tree matching these criteria. This error message is suppressed by redirecting it to device NUL.
Read the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line with using a separate command process started in background.
FOR captures everything output to handle STDOUT of started command process and processes the captured text line by line after started cmd.exe finished.
FOR ignores empty lines which do not occur here at all. FOR would also ignore lines starting with ; because of being the default for end of line option. As DIR outputs the file names with full path, it is not possible that a line starts with ;. But eol=| is nevertheless used to define the vertical bar as end of line which no folder/file name can contain ever.
FOR splits up by default each line into substrings (tokens) using normal space and horizontal tab character as delimiters. This behavior is not wanted here as file path could contain a space character. For that reason delims= is used to define an empty list of delimiters which disables the line splitting behavior.
The inner FOR is used to get assigned to loop variable J just the string left to .1.JPG.
The IF condition checks if there is already a file *.JPG for current file *.1.JPG in same directory as current file in which case the file *.1.JPG is deleted or otherwise the renamed to *.JPG if this file deletion or file rename operation is permitted at all depending on read-only attribute, file permissions of current account and current file sharing access permissions.
But let us assume the image file names can be any file name matching *.jpg and there can be not only *.1.jpg, but also *.2.jpg to *.99.jpg image files, i.e. any number after a dot before file extension .jpg. In this case DIR is not enough to get the list of file names with file extension and full path. It is additionally necessary to use FINDSTR with a regular expression to filter the list of file names.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir "F:\My Pictures\*.*.jpg" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R "\.[0123456789][0123456789]*\.jpg$"') do (
for %%J in ("%%~dpnI") do (
if exist "%%~dpnJ%%~xI" (
del "%%I"
) else (
ren "%%I" "%%~nJ%%~xI"
)
)
)
endlocal
First FINDSTR outputs just lines read from STDIN which
matches case-sensitive because of option /I
the regular expression \.[0123456789][0123456789]*\.jpg$
as explicitly declared with option /R.
The regular expression matches a string consisting of a dot, one or more digits, one more dot and the string jpg found at end of line. So a file name like Hello.World.jpg output by DIR is not matched by FINDSTR and therefore not output by FINDSTR and so not processed by FOR.
But a file name like Hello.World.393.jpg is processed and either deleted or renamed to Hello.World.jpg depending on existence of Hello.World.jpg in same directory.
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.
call /?
del /?
dir /?
echo /?
endlocal /?
findstr /?
for /?
goto /?
if /?
ren /? or rename /?
set /?
setlocal /?
See also Where does GOTO :EOF return to?

How to move files with a keyword with a lookup file using a batch file?

I have a list of IDs in a CSV file stored in a folder. Then I have files which contains these IDs. I want to move the files if it is present in the CSV file.
For example:
I have this documents in Docs folder: Jose_1234_CV.pdf, Jose_111_CV.doc, Jose_2323_CV.doc
I have IDs.csv file containing the following IDs: 1234, 111
When I run the batch job, the files with the IDs 1234 and 111 should be moved to a particular folder.
I have the below code working only if in the look up file contains the exact file name. I want it work even if it only find the ID:
#echo off
set "Source=C:\users\directory"
set "Target=C:\users\target"
set "FileList=C:\users\lookup\ID.csv"
echo/
if not exist "%Source%" echo Source folder "%Source%" not found & goto :Quit
if not exist "%FileList%" echo File list "%FileList%" not found & goto :Quit
2> nul md "%Target%"
for /F usebackq^ delims^=^ eol^= %%a in ("%FileList%") do (
for /F "delims=" %%b in ('dir /B /S /A:-D "%Source%\%%a"') do (
move "%%b" "%Target%"
)
)
:Quit
echo/
echo Press the Space bar to close this window.
pause > nul
This could be done with following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BaseDirectory=C:\Users"
set "Source=%BaseDirectory%\directory"
set "Target=%BaseDirectory%\target"
set "FileList=%BaseDirectory%\lookup\IDs.csv"
set "TempFile=%TEMP%\%~n0.tmp"
if not exist "%Source%\" echo Source folder "%Source%" not found. & goto Quit
if not exist "%Source%\*_*_CV.*" echo Folder "%Source%" does not contain any *_*_CV.* file. & goto Quit
if not exist "%FileList%" echo File list "%FileList%" not found. & goto Quit
md "%Target%" 2>nul
if not exist "%Target%\" echo Folder "%Target%" could not be created. & goto Quit
set "FileMoved=0"
del "%TempFile%" 2>nul
rem Create a copy of list file with each identifer surrounded by underscores
rem to make sure that on an identifier like 111 just the file with _111_ in
rem name and not with _2111_ or _1114_ in name is moved to target folder.
for /F "usebackq delims=" %%I in ("%FileList%") do echo _%%~I_>>"%TempFile%"
rem Move all files containing one of the identifiers in
rem temporary file in its file name to the target folder.
for /F "eol=| delims=" %%I in ('dir "%Source%\*_*_CV.*" /A-D-H /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /L /G:"%TempFile%" 2^>nul') do (
move /Y "%%I" "%Target%\%%~nxI" >nul
if not errorlevel 1 set /A FileMoved+=1
)
rem Delete the temporary file no longer needed.
del "%TempFile%"
rem Delete the target directory if being empty.
if %FileMoved% == 0 rd "%Target%" 2>nul
rem Inform user about number of moved files.
set "Plural_S=s"
if %FileMoved% == 1 set "Plural_S="
echo Moved %FileMoved% *.csv file%Plural_S% to "%Target%".
:Quit
endlocal
echo/
echo Press SPACE to close this window.
pause >nul
This batch code expects that the file C:\Users\lookup\IDs.csv contains the identifiers line by line like:
1234
111
The first FOR command creates a copy of IDs.csv with the lines:
_1234_
_111_
The second FOR executes in a separate command process started with cmd.exe /C in background (= not visible) the command line:
dir "C:\Users\directory\*_*_CV.*" /A-D-H /B /S 2>nul | C:\Windows\System32\findstr.exe /L /G:"C:\Users\UserName\AppData\Local\Temp\BatchFileName.tmp" 2>nul
So the command process in background with no console window is first running command DIR which outputs
all non hidden files because of /A-D-H (attribute not directory and not hidden)
matching the wildcard pattern *_*_CV.*
in bare format because of /B which means file name only
in specified directory and all its subdirectories because of /S
with full path also because of /S
to handle STDOUT.
DIR outputs an error message if it can't find any file matching the wildcard pattern. This error message written to handle STDERR is suppressed by redirecting it to device NUL with 2>nul.
The file names with full path output by DIR line by line to handle STDOUT are redirected with | to handle STDIN of next command FINDSTR.
FINDSTR searches in the list of file names for
any string listed in temporary file specified after /G:
case-sensitive because of not using /I
and literally because of using /L
and outputs the lines (= file names with full path) containing one of the strings in the temporary file.
That means no directory in path of a file should contain _ID_ or otherwise files are moved which do not contain the identifier in file name, but in its path.
It is possible that FINDSTR can't find any line matching the search criteria and therefore outputs an error message to handle STDERR which is suppressed by redirecting it with 2>nul to device NUL.
The command FOR captures the output written to handle STDOUT of the background command process which is the output of DIR filtered with FINDSTR and then processes this output line by line.
Empty lines are ignored by FOR. But that does not matter here as the list of file names each with full path does not contain empty lines.
Lines starting with a semicolon would be also ignored by FOR, but this behavior is modified with eol=| to ignore just lines starting with a vertical bar. A vertical bar is impossible here as no directory or file can contain a vertical bar in name. Well, it is also practically impossible that a file name with full path starts with a semicolon. So eol=| would not be really needed here.
FOR with parameter /F splits by default a line up into substrings using space and horizontal tab as string delimiters and assign only the first space/tab delimited string to specified loop variable I. This split behavior is not wanted here as needed in body command block of FOR is always the full qualified name of a file to move even on path or file name contains a space character. For that reason delims= is specified to define an empty list of delimiters which disables splitting up the lines into substrings.
MOVE is executed which moves the file and exits with 0 on success and with any value greater 0 on an error.
The exit code of an executed application or command is assigned by Windows command interpreter to environment variable ERRORLEVEL.
The IF condition checks if MOVE exited not with a value greater or equal 1, i.e. with 0 (or a negative value) for success. In this case file was moved successfully and the appropriate counter is increased by one.
If the source directory contains never subdirectories, it would be better to use for second FOR loop:
for /F "eol=| delims=" %%I in ('dir "%Source%\*_*_CV.*" /A-D-H /B 2^>nul ^| %SystemRoot%\System32\findstr.exe /L /G:"%TempFile%" 2^>nul') do (
move /Y "%Source%\%%I" "%Target%\%%I" >nul
if not errorlevel 1 set /A FileMoved+=1
)
DIR is here without /S and so output are the non hidden files matching the wildcard pattern without path. For that reason FINDSTR searches in file name only for _ID_. The command MOVE must contain %Source%\ as %%I is in this case just the name of a file without path.
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.
del /?
dir /?
echo /?
endlocal /?
findstr /?
for /?
goto /?
if /?
md /?
move /?
pause /?
rd /?
rem /?
set /?
setlocal /?
Read also the Microsoft article about Using Command Redirection Operators for an explanation of 2>nul and |. The redirection operators > and | must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded command line with using a separate command process started in background.

Trim string content and rename file

In a text file I have 1000 of rows, each row has below data:
ABCDqwqqwq321890821dsapodsaidadopd-9sa-9asd-9sa00sahjkhjsakdhdsddsad JhKyy120320171212
I need to delete the characters (90 characters onwards) from each line
and save file as Test_DDMMYYYY.txt.
Below is my code, I'm not able to get the output.
Any suggestions please?
#echo off &setlocal
(for /f "delims=" %%a in (*.*txt) do (
set "line=%%a"
setlocal enabledelayedexpansion
set "line=!line:~,90!"
echo(!line!
endlocal
))>Test_DDMMYYYY.txt
Also using this code for renaming the txt file:
#echo off
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| find "."') do set "dt=%%a"
set "YY=%dt:~2,2%"
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set datestamp=%DD%%MM%%YYYY%
set fullstamp=%DD%%MM%%YYYY%
ren "C:\test\Test_.txt" "Test_%fullstamp%.txt"
The batch code for copying each line not being empty and not starting with a semicolon to a new file with with local date in format DDMMYYYY in file name with truncating each line after the first 90 characters could be for example:
#echo off
if not exist "Test.txt" goto :EOF
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=2 delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LocalDate=%%I"
set "FileDate=%LocalDate:~6,2%%LocalDate:~4,2%%LocalDate:~0,4%
del "Test_%FileDate%.txt" 2>nul
for /F "usebackq delims=" %%I in ("Test.txt") do set "Line=%%I" & echo !Line:~0,90!>>"Test_%FileDate%.txt"
endlocal
The command
%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE
outputs UTF-16 Little Endian encoded for example:
LocalDateTime=20170513195342.921000+120
So the first line is an empty line and the second line contains the local date in format YYYYMMDD after the equal sign independent on region and languages settings defined for the current user account. And there are output 2 more empty lines.
Command FOR has some problems on parsing UTF-16 LE encoded output which results in interpreting last empty line not as empty line, but as a line containing just a carriage return. Details about this issue can be found for example in answer on Batch: How to correct variable overwriting misbehavior when parsing output.
The command FOR as used here splits each line up into two substrings (tokens) using the equal sign as delimiter because of delims== and assigns just the second string to loop variable I because of tokens=2. This means that 20170513195342.921000+120 is assigned to environment variable LocalDate.
The issue with last empty line being interpreted as line with a carriage return is worked around because of tokens=2 because the line with just carriage return can't be split up into two tokens and for that reason the misinterpreted last empty line of output of WMIC is also ignored here by FOR.
The local date/time string is reformatted to just DDMMYYYY as requested by using command SET with 3 string substitutions.
Hint: File names using YYYYMMDD or better readable YYYY-MM-DD are automatically sorted by date when being displayed sorted by file name. Therefore YYYYMMDD or YYYY-MM-DD would be better than DDMMYYYY as date format in file name.
Then the output file is deleted if existing already with redirecting the error message on output file not existing to device NUL to suppress it.
The second FOR parses the lines of file Test.txt even with enclosing the file name in double quotes because of option usebackq. Empty lines are skipped as also lines starting with a semicolon. But all other lines are processed without splitting the lines because of option delims= which turns off default splitting on spaces and horizontal tabs and processing further only the first substring. So each non empty line not starting with a semicolon is assigned to loop variable I before processing the command line.
The line is next assigned to environment variable Line.
Note 1: Because of enabled delayed expansion as needed next with this solution a line containing 1 or more exclamation marks would not be processed correct because of interpreting each exclamation mark as begin or end of a reference to an environment variable with delayed expansion. So a different batch code would be necessary if the file could contain also exclamation marks in first 90 characters of each line.
Note 2: The posted line in question from input file has only 68 characters and there was not posted what should be written for this line into output file. I hope that I understood the task correct.
With operator & two commands are specified on one command line to execute after the command set "Line=%%I" one more command to output just the first 90 characters from input file with redirecting this output from handle STDOUT to file with local date in file name with appending this line to existing output file contents.
The truncation of the line to first 90 characters within FOR loop requires delayed expansion or a different code like using a subroutine.
Once the input file Test.txt is completely processed line by line, the previous command process environment is restored and batch file execution terminates.
Here is an alternate solution which avoids the usage of delayed expansion:
#echo off
if not exist "Test.txt" goto :EOF
setlocal EnableExtensions DisableDelayedExpansion
for /F "tokens=2 delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LocalDate=%%I"
set "FileDate=%LocalDate:~6,2%%LocalDate:~4,2%%LocalDate:~0,4%
del "Test_%FileDate%.txt" 2>nul
for /F "usebackq delims=" %%I in ("Test.txt") do set "Line=%%I" & call :OutputLine
endlocal
goto :EOF
:OutputLine
>>"Test_%FileDate%.txt" echo %Line:~0,90%
goto :EOF
A subroutine is used here to output just the first 90 characters of each line to the output file.
The redirection operator >> and the output file name are specified first on command line because of
echo %Line:~0,90%>>"Test_%FileDate%.txt"
could result with immediate environment variable expansion and a line ending with a space and a number in range 1 to 9 in wrong file output.
This solution also does not work when a line to output contains characters with special meaning for Windows command interpreter like &, |, <, >, ... Windows command interpreter is not really designed for text editing. Text editors are designed for text editing.
The first solution rewritten to process each *.txt file in current directory except the output file also written into current directory.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "tokens=2 delims==" %%I in ('%SystemRoot%\System32\wbem\wmic.exe OS GET LocalDateTime /VALUE') do set "LocalDate=%%I"
set "FileDate=%LocalDate:~6,2%%LocalDate:~4,2%%LocalDate:~0,4%
del "Test_%FileDate%.txt" 2>nul
for /F "delims=" %%T in ('dir *.txt /A-D /B 2^>nul') do for /F "usebackq delims=" %%I in ("%%T") do set "Line=%%I" & echo !Line:~0,90!>>"Test_%FileDate%.txt"
endlocal
The file contents processing FOR loop is nested within one more FOR loop which runs command DIR listing all *.txt files (and not directories) because of /A-D in bare format (file name and file extension only without path) because of /B.
This list of file names of command DIR is processed line by line by outer FOR without splitting up the file names and running the inner FOR loop which processes the lines of the text file.
2^>nul redirects the error message output by DIR if no *.txt file found in current directory to device NUL to suppress it. The redirection operator > must be escaped here with ^ to be interpreted by Windows command interpreter as literal character on parsing FOR command line, but as redirection operator on executing DIR command line in one more command process started by FOR to execute the command.
2>nul in the command (line) to execute by FOR would result in an exit of batch execution because of a syntax error because a redirection for FOR can't be specified at this position in the command line.
Another variant which processes also each *.txt file in current directory.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
for /F "delims=" %%T in ('dir *.txt /A-D /B 2^>nul') do (
del "%%~nT.tmp" 2>nul
for /F "usebackq delims=" %%I in ("%%T") do set "Line=%%I" & echo !Line:~0,90!>>"%%~nT.tmp"
if exist "%%~nT.tmp" (
%SystemRoot%\System32\fc.exe "%%~nT.tmp" "%%T" /A >nul
if errorlevel 1 ( move /Y "%%~nT.tmp" "%%T" ) else ( del "%%~nT.tmp" )
)
)
endlocal
The truncated lines of each *.txt file are written into a *.tmp file with same name as the current *.txt file instead of writing all truncated lines into same output file.
After processing a *.txt file, the *.tmp file replaces the *.txt file if its content is different to content of the *.txt file. Otherwise the temporary file is deleted on *.tmp file being equal the *.txt file and the text file is kept unmodified.
So finally all *.txt files in current directory have no line longer 90 characters.
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.
call /?
del /?
echo /?
endlocal /?
fc /?
for /?
goto /?
if /?
move /?
set /?
setlocal /?
wmic /?
wmic os /?
wmic os get /?
wmic os get localdatetime /?
Read also Single line with multiple commands using Windows batch file and the Microsoft article about Using Command Redirection Operators.

Resources