There are the subdirectories S and F containing files with the same names but different file sizes (< 2 MB).
I want to copy a file from S to F, exactly if the file from S is smaller than the file from F.
FOR /R %%F IN ( >>THE_FILES_IN_S<< ) DO (
set fileS="S/%%~nF"
FOR /F "usebackq" %%A IN ('%fileS%') DO set sizeS=%%~zA
set fileF="F/%%~nF"
FOR /F "usebackq" %%A IN ('%fileF%') DO set sizeF=%%~zA
if %sizeS% LSS %sizeF% (copy /V /Y %fileS% %fileF%)
)
The code above
does not work because >>THE_FILES_IN_S<< is pseudo-code.
What is the right expression?
Are there other mistakes (and what is the correct form)?
The 32bit signed integer size limitation of ~2GB does not apply
when comparing numbers in strings left padded with zeroes to equal length.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BaseFolder=C:\Temp"
for %%S in ("%BaseFolder%\S\*") do (
if exist "%BaseFolder%\F\%%~nxS" (
for %%F in ("%BaseFolder%\F\%%~nxS"
) do Call :Check %%~zS %%~zF || copy /Y "%%S" "%%F" >nul
)
)
endlocal
Goto :Eof
:Check
Set "S=00000000000000000000%1"
Set "F=00000000000000000000%2"
If %S:~-20% LSS %F:~-20% exit /B 1
The above batch avoids delayed expansion by passing the sizes to a subroutine and comparing strings with 20 decimal places returning an errorlevel on a less result to copy on this fail condition.
The usage of environment variables defined/modified within a command block and referenced in same command block would require the usage of delayed expansion. The help of command SET output on running in a command prompt window set /? explains usage of delayed expansion on an IF and a FOR example.
Best is avoiding usage of delayed expansion by using the loop variables directly instead of assigning their values to environment variables and next reference the values of the environment variables in a command block starting with ( and ending with matching ).
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "BaseFolder=C:\Temp"
for %%S in ("%BaseFolder%\S\*") do (
if exist "%BaseFolder%\F\%%~nxS" (
for %%F in ("%BaseFolder%\F\%%~nxS") do (
if %%~zS LSS %%~zF copy /Y "%%S" "%%F" >nul
)
)
)
endlocal
Replace C:\Temp in third line by real path to base folder. If the batch file is stored in base folder, the third line could be replaced by:
rem Base folder is the directory containing the batch file.
set "BaseFolder=%~dp0"
rem Remove the backslash at end from batch file path.
set "BaseFolder=%BaseFolder:~0,-1%"
The outer FOR searches for any non hidden file in subdirectory S of base folder matching the wildcard pattern * (any name) and assigns the full qualified file name to loop variable S.
If a file with same file name and file extension exists also in subdirectory F of base folder, one more FOR loop is executed which just assigns the already known full qualified file name of current file in subdirectory F to loop variable F and then runs one more IF comparison.
The inner IF compares with a 32-bit signed integer comparison the file sizes of the two files and copies a smaller file in subdirectory S to subdirectory F with suppressing the success message output by COPY to handle STDOUT by redirecting it to device NUL.
Please note that this batch file works only for files with less than 2 GiB because of 32-bit signed integer limitation of Windows command processor on processing integer values.
The entire batch code above could be written also as a single command line:
#for %%S in ("C:\Temp\S\*") do #if exist "C:\Temp\F\%%~nxS" for %%F in ("C:\Temp\F\%%~nxS") do #if %%~zS LSS %%~zF copy /Y "%%S" "%%F" >nul
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 argument 0 – the batch file)
copy /?
echo /?
endlocal /? ... is not really needed here and could be removed.
for /?
if /?
rem /?
set /?
setlocal /? ... is not really needed here and could be removed.
Related
I am using an alass tool to synchronize two subtitles. It is simple to use with one file at a time but I want to use it on multiple files using a loop.
The usage of the tool is like this:
alass.bat correct_subtitle.srt incorrect_subtitle.srt output.srt
I want to do a simple for loop with two parameters with this command:
FOR %i IN (*g.srt) DO FOR %n IN (*t.srt) DO alass.bat %i %n %n
The script is working but I want the command works one time with the second file not looping the first file with all the second files.
I want the script to do like this:
C:\Users\user\Downloads\alass-windows64\alass.bat Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt
C:\Users\user\Downloads\alass-windows64\alass.bat Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt
etc.
All the subtitles are in one folder the correct and incorrect subtitles are like this:
Correct sub (Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt)
incorrect sub (Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt)
Correct sub (Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt)
incorrect sub (Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt)
etc.
A solution for revision 17 of the question is:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
for /F "eol=| delims=" %%I in ('dir *.eng.srt /A-D-L /B /ON 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /R "\.S[0123456789][0123456789]*E[0123456789][0123456789]*\."') do call :ProcessFile "%%I"
endlocal
exit /B
:ProcessFile
echo Correct file: %1
set "FileNameBegin=%~n1"
:GetMatchingPart
for %%J in ("%FileNameBegin%") do (
echo %%~xJ| %SystemRoot%\System32\findstr.exe /I /R "^\.S[0123456789][0123456789]*E[0123456789][0123456789]*$" >nul
if errorlevel 1 set "FileNameBegin=%%~nJ" & goto GetMatchingPart
)
for /F "eol=| delims=" %%J in ('dir "%FileNameBegin%.*-FGT.srt" /A-D /B 2^>nul') do (
echo Incorrect file: "%%J"
call alass.bat %1 "%%J" "%%J"
)
goto :EOF
That code was run on a FAT32 drive with following files in current directory:
Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt
Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt
Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt
Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt
Exa..mple!.S01E01.anotherName.DTS-FGT.srt
Exa..mple!.S01E01.name.eng.srt
example.S01E02.anotherName-FGT.srt
example.S01E02.name.eng.srt
The output without really calling alass.bat is:
Correct file: "Batman.Beyond.S01E01.1080p.BluRay.Remux.eng.srt"
Incorrect file: "Batman.Beyond.S01E01.Rebirth.Part.1.1080p.BluRay.x264.DTS-FGT.srt"
Correct file: "Batman.Beyond.S01E02.1080p.BluRay.Remux.eng.srt"
Incorrect file: "Batman.Beyond.S01E02.Rebirth.Part.2.1080p.BluRay.x264.DTS-FGT.srt"
Correct file: "Exa..mple!.S01E01.name.eng.srt"
Incorrect file: "Exa..mple!.S01E01.anotherName.DTS-FGT.srt"
Correct file: "example.S01E02.name.eng.srt"
Incorrect file: "example.S01E02.anotherName-FGT.srt"
The main FOR loop runs in background one more cmd.exe with option /c the command line within ' appended as additional arguments.
The command DIR executed by this second command processor outputs all names of files in current directory matching the wildcard pattern *.eng.srt.
This list is redirected to FINDSTR which filters the list of file names based on the regular expression \.S[0123456789][0123456789]*E[0123456789][0123456789]*\.. So a file name to process must contain a string consisting of
a dot
case-insensitive the letter S
one or more digits in range 0 to 9
case-insensitive the letter E
one or more digits in range 0 to 9
on more dot.
All the file names ending case-insensitive with .eng.srt and matching the regular expression filter criteria are output by FINDSTR to handle STDOUT of background command process and captured by cmd.exe processing the batch file.
The main FOR loop processes the list of file names line by line after the started cmd.exe process closed itself. File names can contain spaces characters which is the reason for using the option delims= to define an empty list of delimiters to turn off the default line splitting behavior on spaces/tabs. File names can start with a semicolon and for that reason the option eol=| is used to define the vertical bar as end of line character which no file name can contain ever. So each file name is assigned completely to the specified loop variable I
For each file name is called the subroutine ProcessFile which first outputs the current file name with correct subtitles.
Next a FOR loop is used to remove from the file name the string after last dot which is the file extension according to the definition of Microsoft. The "file extension" string is tested with FINDSTR on being the part which is used as identifier and also as separator string between film title and the meta data of the film in file name. If regular expression does not return a positive match on the current "file extension" string, the file name is truncated at end by removing the current "file extension".
Finally after one or more loop runs the beginning of the file name is found consisting of film name with zero or more dots inside and the string matched by the regular expression. So the environment variable FileNameBegin is for the four examples:
Batman.Beyond.S01E01
Batman.Beyond.S01E02
Exa..mple!.S01E01
example.S01E02
That string part is now used to find the matching file with incorrect subtitles ending case-insensitive with the string -FGT.srt. That is again done starting one more cmd.exe to run DIR to find that file.
The usage of the command DIR to get a list of matching file names first loaded into memory can be important depending on what alass.bat does with the passed file names. That is important especially on FAT file systems like FAT32 or exFAT which do not store the file names in an local specific alphabetic order. The file tables of the file system can be changed on each call of alass.bat if this batch file modifies the srt files and that is not good on using FOR directly to process the files. It can result in skipping some srt files or processing some srt files more than once or in worst case even in an endless running loop. That is the reason for using DIR executed by a command process in background to always get a list of matching file names which does not change anymore while the main FOR loop as well as the last FOR loop run the commands which perhaps result in changing the file tables of the file system.
That solution is definitely not the fasted possible, but a very fail-safe solution and should work for all film titles and all file systems independent on what alass.bat does as long as this batch file does not change the current 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 /?
dir /?
echo /?
endlocal /?
exit /?
findstr /?
for /?
goto /?
if /?
set /?
setlocal /?
Here's my approach to v17
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the source directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files\t w o"
FOR %%s IN (0 1) DO FOR /L %%t IN (0 1 9) DO FOR %%e IN (0 1) DO FOR /L %%f IN (0 1 9) DO (
FOR /f "delims=" %%o IN (
'dir /b /a-d "%sourcedir%\*.S%%s%%tE%%e%%f.*T.srt" 2^>nul'
) DO (
SET "name=%%~no"
FOR /f "tokens=1,2delims=/" %%p IN ("!name:.S%%s%%tE%%e%%f.=/!") DO (
FOR %%b IN ("%sourcedir%\%%p.S%%s%%tE%%e%%f.*g.srt") do ECHO CALL alass.bat "%%~fb" "%%~fo" "%sourcedir%\%%p.S%%s%%tE%%e%%f.%%q.srt"
)
)
)
GOTO :EOF
Always verify against a test directory before applying to real data.
As ever, the generated lines are simply echoed.
Essentially, for each S and E 00..19, locate the "T" file (otherwise, no point) split the name on the .SssEee. string and find the {1}.SssEee.*g.srt file
mix and match the parts.
...but I still have problems understanding the destination filename...
EDIT: New better version
I added a new version that I think is the fastest and most convenient way to solve this problem:
#echo off
setlocal EnableDelayedExpansion
rem Process "correct" files with eng.srt extension
for %%i in (*.eng.srt) do (
echo Correct sub: %%i
set "correct=%%i"
rem Search for the prefix of this file
set "name="
set "prefix="
for %%k in ("!correct:.=" "!") do if not defined prefix (
set "part=%%~k"
set "name=!name!.!part!"
rem Check if this part have the "S##E##" end of prefix format
if "!part:~0,1!!part:~3,1!!part:~6!" equ "SE" ( rem "S__E__" letters and length match
set /A "S=E=0, S=1!part:~1,2!-100, E=1!part:~4,2!-100" 2>nul
if !S! gtr 0 if !E! gtr 0 ( rem Both ## numbers match: end of prefix found
rem Process the companion "incorrect" *FGT.srt file
set "prefix=!name:~1!"
set "name="
for %%n in (!prefix!.*FGT.srt) do (
echo Incorrect sub: %%n
echo/
REM call alass.bat %%i %%n %%n
set "name=%%n"
)
if not defined name (
echo Warning: Incorrect sub not found
echo/
)
)
)
)
if not defined prefix (
echo Warning: Bad filename format
echo/
)
)
EDIT: New version for the last OP´s revision
This Batch file should solve this question:
#echo off
setlocal EnableDelayedExpansion
rem Count files with same prefix
for %%a in (*.srt) do (
set "name=%%a"
set "prefix="
for %%b in ("!name:.=" "!") do (
set "prefix=!prefix!%%~b."
set /A "count[!prefix:-=_!]+=1"
)
)
rem Process pairs of files
for /F "tokens=2,3 delims=[]=" %%a in ('set count[') do (
if %%b equ 2 (
set "right="
for %%c in ("%%a*.srt") do (
if not defined right (
set "right=%%c"
) else (
set "wrong=%%c"
)
)
call alass.bat !right! !wrong! !wrong!
)
)
Accordingly to your description: "The files have different names like this: Correct sub (example.S01E01.name.ybg.srt). Incorrect sub (differentExample.S01E01.anotherName.wrt.srt)." That is: correct and incorrect names of the same set have the second dot-separated token the same, like S01E01 or S01E02 in the examples shown.
The Batch file below solve such problem:
#echo off
setlocal
for %%i in (*g.srt) do for /F "tokens=2 delims=." %%k in ("%%i") do (
for %%n in (*.%%k.*t.srt) do (
call alass.bat %%i %%n %%n
)
)
NOTE: This part of the answer relates to Revision 17 of the question.
I would do it with the following batch-file, assuming that the first .-separated parts up to the S??E?? pattern of the file names of a pair of files are the same:
#echo off
setlocal EnableExtensions DisableDelayedexpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (target directory)
set "_PREF=*" & rem // (prefix of base file names)
set "_MASK=S??E??" & rem // (middle part of file names without `.`)
set "_FILT=S[0123456789][0123456789]E[0123456789][0123456789]"
set "_SUFF1=*g.srt" & rem // (suffix of 1st file name with extension)
set "_SUFF2=*T.srt" & rem // (suffix of 2nd file name with extension)
set "_TOOL=%~dp0alass.bat"
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Loop over 1st files:
for %%I in ("%_PREF%.%_MASK%.%_SUFF1%") do (
rem // Reset left part of file name, store currently iterated base name:
set "LEFT=" & set "NAME=%%~nI"
setlocal EnableDelayedExpansion
rem // Loop as many times as there are `.`-separated parts in the base name:
for %%K in ("!NAME:.=" "!") do (
rem // Do the following only as long as the left part is still not found:
if not defined LEFT (
rem // Utilise a `for` loop on the base name to yield `~`-modifiers:
for %%L in ("!NAME!") do (
rem /* Split base name into last part and the rest, the latter
rem of which is going to be used for the next iteration: */
endlocal & set "LAST=%%~xL" & set "NAME=%%~nL"
rem // Determine whether the last part matches the given pattern:
cmd /D /V /C echo(!LAST:~1!| findstr /R /X /I /C:"%_FILT%" > nul && (
rem // Match encountered, so store currently processed path:
set "LEFT=%%~nxL"
)
setlocal EnableDelayedExpansion
)
)
)
rem // Procede further only if a suitable left part of file has been found:
for %%L in ("!LEFT!") do endlocal & if not "%%~L"=="" (
rem // Search for respective 2nd file:
for %%J in ("%%~L.%_SUFF2%") do (
rem /* Store names of both 1st and 2nd file, then call the sub-script
rem utilising the second `%`-expansion established by `call` to
rem avoid doubling of `^`-symbols as well as loss of `%`-signs: */
set "FILE1=%%~I" & set "FILE2=%%~J"
call "%_TOOL%" "%%FILE1%%" "%%FILE2%%" "%%FILE2%%"
rem /* Erase 2nd file to prevent reprocessing of same file pairs in
rem case of re-execution of this script (remove `ECHO` first!): */
ECHO del "%%~I"
)
)
)
rem // Return from target directory:
popd
)
endlocal
exit /B
The trick herein is to use the ~-modifiers (namely ~x and ~n in particular) of for-loop meta-variables to split the file names at . from the back within a loop that iterates as many times as there are .-separated parts in the base names.
This approach correctly handles file names with characters !, ^ and %. You can prove that when you create an interim sub-script alass.bat with the following contents:
#echo off
setlocal DisableDelayedExpansion
echo(%0 %*
endlocal
exit /B
In case the tool alass.bat overwrites the original *T.srt files, which is what I assume, the script deletes the *g.srt files (when removing the upper-case ECHO in front of the related command) in order not to reprocess the same pair of files upon re-execution of the script.
NOTE: This part of the answer relates to Revision 9 of the question.
I would do it with the following batch-file:
#echo off
setlocal EnableExtensions DisableDelayedexpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (target directory)
set "_SUFF1=g" & rem // (suffix for base names of 1st files)
set "_SUFF2=T" & rem // (suffix for base names of 2nd files)
set "_MASK=*%_SUFF1%" & rem // (name search pattern for 1st files)
set "_EXT=.srt" & rem // (extensions for 1st and 2nd files)
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Loop over 1st files:
for %%I in ("%_MASK%%_EXT%") do (
rem // Store base name of currently iterated 1st file:
set "NAME=%%~nI"
setlocal EnableDelayedExpansion
rem /* Build base name of respective 2nd file; temporarily appending `|` to the
rem name (which cannot occur in a file name) ensures to just replace the very
rem last occurrence of the suffix: */
set "REPL=!NAME!|" & set "REPL=!REPL:%_SUFF1%|=%_SUFF2%!"
rem // Skip in case there is no respective 2nd file:
if exist "!REPL!!_EXT!" (
rem /* Call sub-script with 1st and 2nd file as input files and 2nd one also
rem as output file, preventing delayed expansion but utilising the second
rem `%`-expansion phase established by `call` in order to avoid doubling
rem of `^`-symbols as well as loss of `%`-signs: */
REM call "%~dp0alass.bat" "!NAME!!_EXT!" "!REPL!!_EXT!" "!REPL!!_EXT!"
call "%~dp0alass.bat" "%%NAME%%%%_EXT%%" "%%REPL%%%%_EXT%%" "%%REPL%%%%_EXT%%"
rem /* Erase 2nd file to prevent reprocessing of same file pairs in case of
rem re-execution of the script: */
ECHO del "!NAME!!_EXT!"
)
endlocal
)
rem // Return from target directory:
popd
)
endlocal
exit /B
This approach correctly handles file names with characters !, ^ and %. You can prove that when you create an interim sub-script alass.bat with the following contents:
#echo off
setlocal DisableDelayedExpansion
echo(%0 %*
endlocal
exit /B
If you used the commented-out call command line (with the upper-case REM in front), ^-symbols would become doubled and %-signs would become lost.
In case the tool alass.bat (which is assumed to reside in the same location as this script) overwrites the original *T.srt files, which is what I assume, the script deletes the *g.srt files (when removing the upper-case ECHO in front of the related command) in order not to reprocess the same pair of files upon re-execution of the script.
I have some files
afjj.txt
agk.png
beta.tt
Ritj.rar
I use this script to move files in alphabetically order into autogenerated folders
a
|
|----> afjj.txt
|----> agk.png
b
|----> beta.tt
R
|----> Ritj.rar
To do I use this code
#echo off
setlocal
for %%i in (*) do (
set name=%%i
setlocal enabledelayedexpansion
if not "!name!" == "%0" (
set first=!name:~0,1!
md !first! 2>nul
if not "!first!" == "!name!" move "!name!" "!first!\!name!"
)
)
What is problem? If I double-click on this batch, batch doesn't work, not move.
But this batch works from command line.
Why?
NOTE: I use Windows Server 2016
P.S: from command line I use this command and works but not if I double click directly on .bat
move.bat "Z:\# 2020\Alfa\test"
The first mistake is naming the batch file move.bat. It is never a good idea to give a batch file the name of a Windows command because hat cause usually troubles. See also: SS64.com - A-Z index of Windows CMD commands.
The second mistake is using setlocal enabledelayedexpansion inside the loop without a corresponding endlocal also within same loop executed as often as setlocal. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. The command SETLOCAL pushes several data on stack on every iteration of the loop and the data are never popped from stack in same loop on each loop iteration. The result is sooner or later a stack overflow depending on the number of files to process as more and more data are pushed on stack.
The third mistake is the expectation that the current directory is always the directory of the batch file. This expectation is quite often not fulfilled.
The fourth mistake is using a loop to iterate over a list of files which permanently changes on each execution of the commands in body of FOR loop. The loop in code in question works usually on storage media with NTFS as file system, but does not work on storage media using FAT32 or exFAT as file system.
The fifth mistake is the expectation that %0 expands always to name of the currently executed batch file with file extension, but without file path which is not true if the batch file is executed with full qualified file name (drive + path + name + extension), or with just file name without file extension, or using a relative path.
The sixth mistake is not enclosing the folder name on creation of the subfolder in double quotes which is problematic on file name starting unusually with an ampersand.
The seventh mistake is not taking into account to handle correct file names starting with a dot like .htaccess in which case the second character must be used as name for the subfolder, except the second character is also a dot. It is very uncommon, but also possible that file name starts with one or more spaces. In this case also the first none space character of file name should be used as Windows by default prevents the creation of a folder of which name is just a space character.
The solution is using following commented batch file with name MoveToFolders.cmd or MyMove.bat.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Get folder path of batch file assigned to an environment
rem variable. This folder path ends always with a backslash.
set "FilesFolder=%~dp0"
rem Optionally support calling of this batch file with another folder
rem path without checking if the passed string is really a folder path.
if not "%~1" == "" set "FilesFolder=%~1"
rem Replace all / by \ as very often people use / as directory separator
rem which is wrong because the directory separator is \ on Windows.
set "FilesFolder=%FilesFolder:/=\%"
rem The folder path should end always with a backslash even on folder
rem path is passed as an argument to the batch file on calling it.
if not "%FilesFolder:~-1%" == "\" set "FilesFolder=%FilesFolder%\"
rem Get a list of files in specified folder including hidden files loaded
rem into the memory of running command process which does not change on
rem the iterations of the loop below. Then iterate over the files list and
rem move the files to a subfolder with first none dot and none space character
rem of file name as folder name with the exception of the running batch file.
for /F "eol=| delims=" %%i in ('dir "%FilesFolder%" /A-D /B 2^>nul') do if /I not "%FilesFolder%%%i" == "%~f0" (
set "FileName=%%i"
set "FirstPart="
for /F "eol=| delims=. " %%j in ("%%i") do set "FirstPart=%%j"
if defined FirstPart (
setlocal EnableDelayedExpansion
set "TargetFolderName=%FilesFolder%!FirstPart:~0,1!"
md "!TargetFolderName!" 2>nul
if exist "!TargetFolderName!\" move "%FilesFolder%!FileName!" "!TargetFolderName!\"
endlocal
)
)
rem Restore the previous execution environment.
endlocal
The batch file can be started also with a folder path as argument to process the files in this folder without checking if the passed argument string is really referencing an existing folder.
Please read very carefully the answers on How to replace a string with a substring when there are parentheses in the string if there is interest on how to verify if a passed argument string really references an existing folder.
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 /?
dir /?
echo /?
endlocal /?
for /?
if /?
md /?
move /?
rem /?
set /?
setlocal /?
Here is a slightly amended script. Notice I removed the !first! variable as it is not required. I also built in a safety measure, if it is unable to pushd to the given directory you passed to %~1 it will not continue the move. Else it might move files in a path you do not want it to move files. i.e the working dir you started the script in.
#echo off
setlocal enabledelayedexpansion
pushd "%~1" && echo( || goto :end
for %%i in (*.test) do (
set "name=%%~ni"
if not "%%~nxi" == "%~nx0" (md !name:~0,1!)2>nul
if not "!name:~0,1!" == "%%~i" move "%%~i" "!name:~0,1!\%%~i"
)
popd
goto :eof
:end
echo The directory you entered does not exist. Exited script..
pause
Note, with the above you can also drag a directory to the batch file, which will process that directory.
Or if you plan on double clicking, without parameters from cmd.
#echo off
setlocal enabledelayedexpansion
for %%i in (*.test) do (
set "name=%%~ni"
if not "%%~nxi" == "%~nx0" (md !name:~0,1!)2>nul
if not "!name:~0,1!" == "%%~i" move "%%~i" "!name:~0,1!\%%~i"
)
pause
A slightly different take.
This script will work on the current directory if double clicked, or run at the command line without a path specified.
But it will also allow you to provide a path to it as well.
#( SETLOCAL ENABLEDELAYEDEXPANSION
ECHO OFF
SET "_PATH=%~1"
IF NOT DEFINED _PATH SET "_PATH=%CD%"
)
CALL :Main
( Endlocal
Exit /B )
:Main
For /F "Tokens=*" %%_ in ('
DIR /B/A-D-S-H-L "%path%"
') DO (
IF /I "%%_" NEQ "%~nx0" (
SET "_TmpName=%%_"
IF NOT EXIST "%_Path%\!_TmpName:~0:1!\" MD "%_Path%\!_TmpName:~0:1!\"
MOVE /Y "%_Path%\!_TmpName!" "%_Path%\!_TmpName:~0:1!\!!_TmpName!"
)
)
GOTO :EOF
Based upon only filenames beginning with alphabetic characters, here's a somewhat simpler methodology:
#Echo Off
SetLocal EnableExtensions
PushD "%~1" 2> NUL
If /I "%~dp0" == "%CD%\" (Set "Exclusion=%~nx0") Else Set "Exclusion="
For %%G In (A B C D E F G H I J K L M N O P Q R S T U V W X Y Z) Do (
%SystemRoot%\System32\Robocopy.exe . %%G "%%G*" /Mov /XF "%Exclusion%" 1> NUL 2>&1
RD %%G 2> NUL
)
PopD
Let's say I have a couple of images and I need to rename them and on every iteration add an incremented number.
For this situation I have three images no matter how they name is and I want to rename them like this.
1239.jpg => file1.jpg
file.jpg => file2.jpg
image.jpg => file3.jpg
My commands executed in a command prompt window for this task are:
setlocal EnableDelayedExpansion
set filename=file
set counter=1
for /f "usebackq delims=*" %i in ('dir /b *.jpg') do (set /a counter+=1 ren "%i" "%filename%!counter!.jpg")
But this results in the error message Missing operator.
Can anyone help me with this?
The commands SETLOCAL and ENDLOCAL can be used only in a batch file. Please read this answer for details about the commands SETLOCAL and ENDLOCAL. These two commands do nothing on being executed in a command prompt window. It is necessary to start cmd.exe with option /V:ON to use delayed expansion in a command prompt window as explained by the help output on running cmd /? in a command prompt window.
The usage of usebackq requires enclosing the command line to be executed in ` instead of ' as usual. usebackq is mainly used for processing the lines of a text file of which name is specified in the round brackets enclosed in ".
The following command line with the two commands SET and REN is not of valid syntax. The command SET interprets everything after /a as arithmetic expression to evaluate. In this case the expression misses an operator between 1 and ren whereby ren would be interpreted here as name of an environment variable and not as command to execute next after set.
(set /a counter+=1 ren "%i" "%filename%!counter!.jpg")
The valid command line would be:
set /A "counter+=1" & ren "%i" "%filename%!counter!.jpg"
Enclosing the arithmetic expression in double quotes makes it clear for command SET where the arithmetic expression starts and where it ends. The conditional execution operator & is interpreted by Windows command processor before executing the command SET and results in execution of command REN after command SET even on SET would fail to evaluate the arithmetic expression.
A file renaming task done with Windows command processor is no easy to achieve if
the file extension of the files should not change and
files with any name including those with one or more &()[]{}^=;!'+,`~ should be supported and
there can be already files in the directory with one of the new file names.
For testing the batch file below I created first in a directory following files:
file.jpg
file1.jpg
file2.jpg
file3.jpg
file 4.jpg
File8.jpg
hello!.jpg
image.jpg
The directory was on a FAT32 drive. The file systems FAT16, FAT32 and exFAT return a list of matching directory entries not sorted by name as NTFS which means the list output by command DIR in the main FOR loop in code below is in an unsorted and therefore unpredictable order.
It would be of course possible to append the DIR option /ON to get the list of file names ordered by DIR according to name, but in fact that is not real help in this case, especially because of DIR makes a strict alphabetical sort and not an alphanumeric sort.
A strict alphabetic sort returns a list of ten file names as file1.jpg, file10.jpg, file2.jpg, file3.jpg, ..., file9.jpg while an alphanumeric sort returns a list of ten file names as file1.jpg, file2.jpg, file3.jpg, ..., file9.jpg, file10.jpg.
So here is the commented batch file for this file rename task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FileName=file"
rem The user can run this batch file with a folder path in which all *.jpg
rem files should be renamed with an incremented number. Otherwise the
rem directory of the batch file is search for *.jpg files to rename.
if not "%~1" == "" (
pushd "%~1" || exit /B
) else (
pushd "%~dp0" || exit /B
)
set "FileCount=0"
set "DelayedLoopCount=0"
set "DelayedRenameCount=0"
rem Remove all existing environment variables in local environment of which
rem name starts with DelayedRename_ whereby the underscore is very important
rem because there is also the environment variable DelayedRenameCount.
for /F "delims==" %%I in ('set DelayedRename_ 2^>nul') do set "%%I="
rem Get a captured list of all *.jpg files in current directory and then
rem rename one file after the other if that is possible on no other file
rem has by chance already the new file name for the current file.
for /F "eol=| delims=" %%I in ('dir *.jpg /A-D /B 2^>nul') do call :RenameFile "%%I"
goto DelayedRenameLoop
:RenameFile
set /A FileCount+=1
set "NewName=%FileName%%FileCount%%~x1"
rem Has the file case-sensitive already the right name?
if %1 == "%NewName%" goto :EOF
rem Is the new file name the same as the current name
rem with exception of the case of one or more letters?
if /I %1 == "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
goto :EOF
)
rem Is there no other file which has already the new name?
if not exist "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
goto :EOF
)
rem Another file or folder has already the new name. Remember the name
rem of this file and the new file name with an environment variable for
rem a delayed rename after all other files have been renamed as far as
rem possible.
set /A DelayedRenameCount+=1
set "DelayedRename_%DelayedRenameCount%=|%~1|%NewName%"
goto :EOF
rem It could happen that "file15.jpg" should be renamed to "file3.jpg"
rem while "file3.jpg" exists already which should be renamed to "file12.jpg"
rem while "file12.jpg" exists already which should be renamed to "file20.jpg".
rem This extra loop is used for such worst case scenarios which is executed
rem in a loop until all files have been renamed with a maximum of 50 loop
rem runs in case of one file cannot be renamed and therefore blocking
rem renaming of another file. An endless running loop should be avoided.
rem A file cannot be renamed if a folder has by chance the new file name.
rem A file cannot be renamed if an application has opened the file with
rem a sharing access mode preventing the rename of the file as long as
rem being opened by this application.
:DelayedRenameLoop
if %DelayedRenameCount% == 0 goto EndBatch
for /F "tokens=1-3 delims=|" %%I in ('set DelayedRename_ 2^>nul') do if not exist "%%K" (
echo Rename "%%J" to "%%K"
ren "%%J" "%%K"
set "%%I"
set /A DelayedRenameCount-=1
)
set /A DelayedLoopCount+=1
if not %DelayedLoopCount% == 50 goto DelayedRenameLoop
:EndBatch
popd
endlocal
This batch file output on execution:
Rename "file3.jpg" to "file4.jpg"
Rename "file 4.jpg" to "file5.jpg"
Rename "File8.jpg" to "file6.jpg"
Rename "hello!.jpg" to "file7.jpg"
Rename "image.jpg" to "file8.jpg"
Rename "file2.jpg" to "file3.jpg"
Rename "file1.jpg" to "file2.jpg"
Rename "file.jpg" to "file1.jpg"
The files in the directory were finally:
file1.jpg
file2.jpg
file3.jpg
file4.jpg
file5.jpg
file6.jpg
file7.jpg
File8.jpg
What about the last file?
It has the file name File8.jpg instead of file8.jpg although executed was ren "image.jpg" "file8.jpg". Well, FAT32 is a bit problematic regarding to updates of the file allocation table on a table entry changes only in case of one or more letters.
The solution is using this batch file with two extra FOR loops with # as loop variable and optimized by removing the comments.
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FileName=file"
if not "%~1" == "" (pushd "%~1" || exit /B) else (pushd "%~dp0" || exit /B)
set "FileCount=0"
set "DelayedLoopCount=0"
set "DelayedRenameCount=0"
for /F "delims==" %%I in ('set DelayedRename_ 2^>nul') do set "%%I="
for /F "eol=| delims=" %%I in ('dir *.jpg /A-D /B 2^>nul') do call :RenameFile "%%I"
goto DelayedRenameLoop
:RenameFile
set /A FileCount+=1
set "NewName=%FileName%%FileCount%%~x1"
if %1 == "%NewName%" goto :EOF
if /I %1 == "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
goto :EOF
)
if not exist "%NewName%" (
echo Rename %1 to "%NewName%"
ren %1 "%NewName%"
for %%# in ("%NewName%") do if not "%%~nx#" == "%NewName%" ren "%%~nx#" "%NewName%"
goto :EOF
)
set /A DelayedRenameCount+=1
set "DelayedRename_%DelayedRenameCount%=|%~1|%NewName%"
goto :EOF
:DelayedRenameLoop
if %DelayedRenameCount% == 0 goto EndBatch
for /F "tokens=1-3 delims=|" %%I in ('set DelayedRename_ 2^>nul') do if not exist "%%K" (
echo Rename "%%J" to "%%K"
ren "%%J" "%%K"
for %%# in ("%%K") do if not "%%~nx#" == "%%K" ren "%%~nx#" "%%K"
set "%%I"
set /A DelayedRenameCount-=1
)
set /A DelayedLoopCount+=1
if not %DelayedLoopCount% == 50 goto DelayedRenameLoop
:EndBatch
popd
endlocal
The result of this enhanced batch file is even on FAT32:
file1.jpg
file2.jpg
file3.jpg
file4.jpg
file5.jpg
file6.jpg
file7.jpg
file8.jpg
The reason for using | as string separator on execution of
set "DelayedRename_%DelayedRenameCount%=|%~1|%NewName%"
resulting, for example, in execution of
set "DelayedRename_1=|file.jpg|file1.jpg"
set "DelayedRename_2=|file1.jpg|file2.jpg"
set "DelayedRename_3=|file2.jpg|file3.jpg"
is that the vertical bar is not allowed in a file folder name. So it is a very good character to separate the name of the environment variable with the equal sign appended from current file name and from new file name. This makes it possible to use later delims=| for renaming the file and deleting the environment variable.
See also the Microsoft documentations:
Naming Files, Paths, and Namespaces
Using command redirection operators
The equal sign is allowed in a file name. It is even possible that a *.jpg file has as file name =My Favorite Picute=.jpg which is another reason for using | to get executed for example
set "DelayedRename_4=|=My Favorite Picute=.jpg|file9.jpg"
which later results in assigned DelayedRename_4= to loop variable I, =My Favorite Picute=.jpg to loop variable J and file9.jpg to loop variable K in the FOR loop doing the delayed file renames.
Note: Each FOR loop with '...' in the round brackets results
in starting in background one more command process with %ComSpec% /c '...' and
capturing the output written to handle STDOUT like the output of the cmd.exe internal commands DIR and SET
while cmd.exe processing the batch file waits until started cmd.exe terminated (closed) itself after execution of the command line
and then processing the captured lines one after the other by FOR with ignoring empty lines and lines starting with the defined end of line character after doing the string delimiting which is the reason why eol=| is used on main FOR loop as a file name can start with default end of line character ; and which of course should not be ignored here.
The redirection operator > must be escaped with caret character ^ on those FOR command lines to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir or set command line in the separate command process started in background.
The batch file does not use delayed expansion as this would cause troubles on a file name having one or more exclamation marks which would be interpreted as beginning/end of a delayed expanded environment variable reference on command lines like ren "%%J" "%%K". Therefore a subroutine is used for the main file rename loop on which it is necessary to access the two incremented counter values.
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 /?
dir /?
echo /?
endlocal /?
exit /?
goto /?
if /?
popd /?
pushd /?
rem /?
ren /?
set /?
setlocal /?
I suggest further to look on:
Microsoft documentation for the Windows Commands
SS64.com - A-Z index of Windows CMD commands
Where does GOTO :EOF return to?
Single line with multiple commands using Windows batch file
Symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files
How does the Windows Command Interpreter (CMD.EXE) parse scripts?
Why is no string output with 'echo %var%' after using 'set var = text' on command line?
I have a folder with four to five text files in it.
My overall aim is the following: Create one big file which has the content of the separate files, but in the right order.
I can use the time-stamp of each file to start with the oldest file up to the youngest.
My process right now looks like this:
Order the files in this folder by date.
Create a temporary file and write the content from the separate files into this file.
Output the temporary file.
In code I do something like this:
set temp_concat=%temp_dir%\temp_concat.log
echo %temp_concat%
echo aiu_logs > %temp_concat%
for /f "delims=" %%? in ('dir /b /o:d %Folder%*') do (
for /f "delims=" %%K in (%Folder%%%?) do (
echo %%K >>%temp_concat%
)
)
The above code seems to work as my temp_concat is very large.
However, this takes much much longer than expected. I have to wait about 40 seconds just to merge three files in my case.
Is there some better way of merging some amount of files, but keep them in the correct order by date?
This batch file uses the suggestion posted by Sqashman to use a FOR loop to create the arguments string for command COPY used to concatenate the file contents into a single file in the order of oldest modified file first and newest modified file last.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Folder=%~dp0"
if not "%~1" == "" set "Folder=%~1"
set "Folder=%Folder:/=\%"
if not "%Folder:~-1%" == "\" set "Folder=%Folder%\"
set "ResultsFile=%Folder%Results.log"
del "%ResultsFile%" 2>nul
set "Arguments="
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /O:D "%Folder%*" 2^>nul') do if not "%%~fI" == "%~f0" set "Arguments=!Arguments! + "%%I""
if defined Arguments (
echo aiu_logs>"%ResultsFile%"
copy /B "%ResultsFile%"%Arguments% "%ResultsFile%" >nul
)
endlocal
The batch file as is does not work if either the folder path or one of the file names contains one or more exclamation marks ! because of an enabled delayed environment variable expansion.
Further the command line length is limited and so this batch file does not work on too many files must be concatenated depending on length of the file path of each file and the length of the file names.
A better solution would be using following batch file:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "Folder=%~dp0"
if not "%~1" == "" set "Folder=%~1"
set "Folder=%Folder:/=\%"
pushd "%Folder%" 2>nul
if errorlevel 1 goto EndBatch
set "ResultsFile=Results.log"
del "%ResultsFile%" 2>nul
set "Arguments="
for /F "eol=| delims=" %%I in ('dir /A-D-H /B /O:D * 2^>nul') do if not "%%~fI" == "%~f0" call set "Arguments=%%Arguments%% + "%%I""
if defined Arguments (
echo aiu_logs>"%ResultsFile%"
copy /B "%ResultsFile%"%Arguments% "%ResultsFile%" >nul
)
popd
:EndBatch
endlocal
A folder path with one or more exclamation marks is no problem anymore. Also the file names can contain ! because of delayed expansion is not used by this batch file which is a bit slower than the first batch file.
The folder with the files to concatenate is made the current directory by this batch file. For that reason more file names can be specified as arguments on COPY command line in comparison to first batch file because of the file names are specified without path. But the number of file contents which can be merged with this batch file is nevertheless limited by the maximum length of a Windows command line respectively the maximum length of an environment variable value.
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 /?
copy /?
dir /?
echo /?
endlocal /?
for /?
goto /?
if /?
popd /?
pushd /?
set /?
setlocal /?
Read also the Microsoft article about Using command redirection operators for an explanation of > and 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 DIR command line between the two ' appended as further arguments.
The second FOR /F does not contain a command. It contains a filename. I have not tested this, but perhaps:
set temp_concat=%temp_dir%\temp_concat.log
echo %temp_concat%
echo aiu_logs > "%temp_concat%"
for /f "delims=" %%? in ('dir /b /o:d "%Folder%"') do (
if not "%%~f?" == "%~f0" (
type %%? >>"%temp_concat%"
)
)
This will concatenate all files in the "%Folder%" directory. Paths should be quoted in case there are special characters in them.
our system is going to be migrated from Linux to Windows machine so I'm preparing a batch file equivalent to our existing script. I already have created the batch file but I need to unwrap first the file before processing its next line of codes.
Example. Here is a one-liner wherein the delimiter is "{".
Note: Delimiter can be any or variable character except element delimiter ("~" in this case).
ISA~00~ ~00~ ~ZZ~SAMSUNGSND ~14~181087842 ~130214~2300~U~00401~000000003~0~T~>{GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010{ST~997~131250001{AK1~SC~1809{AK9~A~1~1~1{SE~4~131250001{GE~1~810{IEA~1~000000001
I need it to be unwrapped like this (equivalent to tr "{" "\n" < FileName.txt ):
ISA~00~ ~00~ ~ZZ~SAMSUNGSND ~14~181087842 ~130214~2300~U~00401~000000003~0~T~>
GS~FA~181087842TEST~SYNNTEST~20130214~2300~810~X~004010
ST~997~131250001
AK1~SC~1809
AK9~A~1~1~1
SE~4~131250001
GE~1~810
IEA~1~000000001
EDIT:
Once unwrapped, I need to search fixed values of third field if equal to "1145837" under GS segment (2nd line) and replace it with "1119283" (which is equivalent to sed '/^GS/ s/1145837/1119283/').
Below is my batch file. I need the code to be inserted somewhere inside :WriteToLogFile subroutine
#echo on
::This ensures the parameters are resolved prior to the internal variable
SetLocal EnableDelayedExpansion
rem Get current date and time as local time.
for /f "delims=" %%a in ('wmic OS Get localdatetime ^| %SystemRoot%\System32\Find.exe "."') do set dt=%%a
rem Reformat the date and time strong to wanted format.
set "YYYY=%dt:~0,4%"
set "MM=%dt:~4,2%"
set "DD=%dt:~6,2%"
set "HH=%dt:~8,2%"
set "Min=%dt:~10,2%"
set "Sec=%dt:~12,2%"
set "TimeStamp=%YYYY%-%MM%-%DD%_%HH%-%Min%-%Sec%"
rem Define name of the list file containing current date and time in name.
set "ListFile=FLIST_%TimeStamp%.lst"
rem Change directory (and drive).
cd /D "C:\VP"
rem Create the list file which is good here as the list of files changes
rem while running this batch file and therefore it is better to work with
rem a list file instead of running a FOR directly for each file in the
rem directory. The list file is not included in this list as it either does
rem not exist at all or it has wrong file extension as only *.txt files are
rem listed by command DIR. The log file EDI.log has also wrong file extension.
dir *.txt /A:-D /B /O:D >"C:\VP\TEST\%ListFile%"
rem It might be useful to delete the log file from a previous run.
if exist EDI.log del EDI.log
rem Process each file in the list file.
cd /D "C:\VP\TEST"
for /F "delims=" %%F in ( %ListFile% ) do call :ProcessFile "%%F"
cd /D "C:\VP"
::rem Delete the list file as not needed anymore. It could be also kept.
::del %ListFile%
rem Exit batch file.
endlocal
goto :EOF
:ProcessFile
rem The parameter passed from first FOR is the file name in double quotes.
set "FileName=%~1"
rem Ignore the files CNtable.txt and Dupfile.txt in same directory.
rem Command goto :EOF just exits here from subroutine ProcessFile.
if "%FileName%"=="CNtable.txt" goto :EOF
if "%FileName%"=="Dupfile.txt" goto :EOF
if "%FileName%"=="VanPointAS2in.bat" goto :EOF
if "%FileName%"=="VP.bat" goto :EOF
rem Get 7th, 9th and 14th element from first line of current file.
cd /D "C:\VP"
for /f "usebackq tokens=7,9,14 delims=~*^" %%a in ( "%FileName%" ) do (
set "ISAsender=%%a"
set "ISAreceiver=%%b"
set "ISActrlnum=%%c"
goto WriteToLogFile
)
:WriteToLogFile
rem Remove all spaces as ISAsender and ISAreceiver have
rem usually spaces appended at end according to example
rem text. Then write file name and the 3 values to log file.
set "ISAsender=%ISAsender: =%"
set "ISAreceiver=%ISAreceiver: =%"
set "ISActrlnum=%ISActrlnum: =%"
echo %FileName%,%ISAsender%,%ISAreceiver%,%ISActrlnum%>>"C:\VP\TEST\EDI.log"
set "FLAG=N"
if "%ISAsender%"=="APPLESND" (
if "%ISAreceiver%"=="MANGO" (
set "FLAG=Y"
set "VW=AP"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="APPLESND" (
if "%ISAreceiver%"=="MANGOES" (
set "FLAG=Y"
set "VW=AP"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "APPLE"
echo Moved %FileName% to directory APPLE.
)
)
if "%ISAsender%"=="SAMSUNGSND" (
if "%ISAreceiver%"=="MANGO" (
set "FLAG=Y"
set "VW=SS"
call :DupCheck
echo %errorlevel%>>"C:\VP\TEST\EDI.log"
if errorlevel 1 move /Y "%FileName%" "SAMSUNG"
echo Moved %FileName% to directory SAMSUNG.
)
)
rem Move to directory BYPASS if all else not satisfied.
if "%FLAG%"=="N" (
move /Y "%FileName%" "BYPASS"
echo Moved %FileName% to directory BYPASS
)
rem Exit the subroutine WriteToLogFile.
goto :EOF
:DupCheck
rem Check for ISA control number in file %VW%_table.txt.
%SystemRoot%\System32\Findstr.exe /X /M /C:%ISActrlnum% "C:\VP\TEST\%VW%_table.txt" >nul
if errorlevel 1 goto NewControl
rem This ISA control number is already present in file %VW%_table.txt.
echo Duplicate control %ISActrlnum% found in file %FileName%.
echo %ISActrlnum%,%FileName%>>"C:\VP\TEST\Dupfile.txt"
move /Y "%FileName%" "DUPLICATES"
echo Moved %FileName% to directory DUPLICATES.
rem Exit the subroutine DupCheck.
goto :EOF
:NewControl
echo %ISActrlnum%>>"C:\VP\TEST\%VW%_table.txt"
Any help is appreciated.
Manipulating text files with native batch commands is rather tricky, and quite slow. Most tasks can be done, but it requires quite a few advanced batch techniques to make the solution robust.
You will probably be most happy with GnuWin32 - a free collection of unix utilities for Windows. You could then manipulate file content with familiar tools.
Another good alternative (my favorite - no surprise since I wrote it) is to use REPL.BAT - a hybrid JScript/batch utility that performs a regex search/replace operation on stdin and writes the result to stdout. It is pure script that will run natively on any Windows machine from XP forward. Full documentation is embedded within the script.
I recommend replacing your line delimiter with \r\n rather than \n, as that is the Windows standard for newlines.
Assuming REPL.BAT is in your current directory, or somewhere within your PATH, then the following will make your needed changes:
set "file=fileName.txt"
type "fileName.txt" | repl "{" "\r\n" lx >"%file%.new"
move /y "%file%.new" "%file%" >nul
:GS_replace
<"%file%" call repl "^(GS~.*)1145837" "$11119283" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
I'm concerned that your string of numeric digits could be embedded within a larger number, leading to an unwanted substitution. You might want to refine your search term to prevent this.
The following would only replace an entire field:
:GS_replace
<"%file%" call repl "^(GS~(?:.*~)*)1145837(~|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
The following would only replace an entire number that may be embedded within a larger alpha-numeric string:
:GS_replace
<"%file%" call repl "^(GS~(?:.*\D)*)1145837(\D|$)" "$11119283$2" >"%file%.new"
set "rtn=%errorlevel%"
move /y "%file%.new" "%file%" >nul
if %rtn% equ 0 goto GS_replace
In your comment below, you say you want to restrict the number change to the 3rd field of GS lines. (This is quite different than what you stated in your original question.) This is much simpler - no loop is required:
type "%file%" | repl "^(GS~(.*?~){2})1145837(~|$)" "$11119283$2" >"%file%.new"
move /y "%file%.new" "%file%" >nul