Fix moving and consolidating folders when use batch file process - windows

I have this code
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "SPLITCHAR=-" & rem // (a single character to split the file names)
set "SEARCHSTR=_" & rem // (a certain string to be replaced by another)
set "REPLACSTR= " & rem // (a string to replace all found search strings)
set "OVERWRITE=" & rem // (set to non-empty value to force overwriting)
rem // Get file location and pattern from command line arguments:
set "LOCATION=%~1" & rem // (directory to move the processed files into)
set "PATTERNS=%~2" & rem // (file pattern; match all files if empty)
rem /* Prepare overwrite flag (if defined, set to character forbidden
rem in file names; this affects later check for file existence): */
if defined OVERWRITE set "OVERWRITE=|"
rem // Continue only if target location is given:
if defined LOCATION (
rem // Create target location (surpress error if it already exists):
2> nul md "%LOCATION%"
rem /* Loop through all files matching the given pattern
rem in the current working directory: */
for /F "eol=| delims=" %%F in ('dir /B "%PATTERNS%"') do (
rem // Process each file in a sub-routine:
call :PROCESS "%%F" "%LOCATION%" "%SPLITCHAR%" "%SEARCHSTR%" "%REPLACSTR%"
)
)
endlocal
exit /B
:PROCESS
rem // Retrieve first argument of sub-routine:
set "FILE=%~1"
rem // Split name at (first) split character and get portion in front:
for /F "delims=%~3" %%E in ("%~1") do (
rem // Append a split character to partial name:
set "FOLDER=%%E%~3"
)
setlocal EnableDelayedExpansion
rem // Right-trim partial name:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4%~3=!"
set "FOLDER=!FOLDER:%~3=!"
rem /* Check whether partial name is not empty
rem (could happen if name began with split character): */
if defined FOLDER (
rem // Replace every search string with another:
if not "%~4"=="" set "FOLDER=!FOLDER:%~4=%~5!"
rem // Create sub-directory (surpress error if it already exists):
2> nul md "%~2\!FOLDER!"
rem /* Check if target file already exists; if overwrite flag is
rem set (to an invalid character), the target cannot exist: */
if not exist "%~2\!FOLDER!\!FILE!%OVERWRITE%" (
rem // Move file finally (surpress `1 file(s) moved.` message):
1> nul move /Y "!FILE!" "%~2\!FOLDER!"
)
)
endlocal
exit /B
I use Command Prompt in this way to create folders and move files inside from folder1 to folder2
cd /D "C:\Users\Administrator\Downloads\"
"C:\Users\Administrator\Downloads\test1\build-folder-hierarchy.bat" "C:\Users\Administrator\Downloads\test2" "*.mkv"
What is the problem ?
But I want to get a folder consolidation from files moving, not generates same number of folders from files
The.Race.Corsa.Mortale.2019.S1E02.Episodio2.HDTV.AAC.iTA.X264-ARSENAL.mkv
The.Race.Corsa.Mortale.2019.S1E01.Episodio1.HDTV.AAC.iTA.X264-ARSENAL.mkv
The.Feed.1x05.Episodio.5.ITA.DLMux.x264-UBi.mkv
The.Feed.1x04.Episodio.4.ITA.DLMux.x264-UBi.mkv
The.Feed.1x03.Episodio.3.ITA.DLMux.x264-UBi.mkv
The.Feed.1x02.Episodio.2.ITA.DLMux.x264-UBi.mkv
The.Feed.1x01.Episodio.1.ITA.DLMux.x264-UBi.mkv
Swamp.Thing.1x10.La.Resa.Dei.Conti.ITA.DLMux.x264-UBi.mkv
Volevo.Fare.La.Rockstar.1x11.Confusione.ITA.WEBRip.x264-UBi.mkv
Volevo.Fare.La.Rockstar.1x07.Tabu.ITA.WEBRip.x264-UBi.mkv
Volevo.Fare.La.Rockstar.1x01.Buon.Compleanno.Olly.ITA.WEBRip.x264-UBi.mkv
Volevo Fare La Rockstar 1x12 La Tribu Ita Webrip x264-Ubi.mkv
Virgin.River.1x10.Finali.inattesi.720p.iTA.AAC.DLRip.x265.-.T7.mkv
Virgin.River.1x07.A.dire.il.vero.720p.iTA.AAC.DLRip.x265.-.T7.mkv
Virgin.River.1x04.Un.Cuore.Ferito.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
Virgin.River.1x01.La.Vita.Continua.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
Tre.Giorni.Di.Natale.1x03.Episodio.3.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
Tre.Giorni.Di.Natale.1x01.Episodio.1.iTA.AC3.WEBMux.x264-ADE.CreW.mkv
But I want to get folders and move files in this way
├─The Race Corsa Mortale [folder]
│ ├─The.Feed.1x05.Episodio.5.ITA.DLMux.x264-UBi [file]
│ ├─The.Feed.1x04.Episodio.4.ITA.DLMux.x264-UBi [file]
└─ ....
├─Virgin River [folder]
│ └─Virgin.River.1x07.A.dire.il.vero.720p.iTA.AAC.DLRip.x265 [file]
:
I try also to use this batch script but it doesn't work: I click on in via explorer but is like disactivated (I use Windows Server 2012)
#echo off
setlocal EnableDelayedExpansion
rem Change current directory to the one where this .bat file is located
cd "%~P0"
set "digits=0123456789"
rem Process all *.mkv files
for %%f in (*.mkv) do (
rem Get the folder name of this file
call :getFolder "%%f"
rem If this file have a properly formatted name: "headS##Etail"
if defined folder (
rem Move the file to such folder
if not exist "!folder!" md "!folder!"
move "%%f" "!folder!"
)
)
goto :EOF
:getFolder file
set "folder="
set "file=%~1"
set "head="
set "tail=%file%"
:next
for /F "delims=%digits%" %%a in ("%tail%") do set "head=%head%%%a"
set "tail=!file:*%head%=!"
if not defined tail exit /B
if /I "%head:~-1%" equ "S" goto found
:digit
if "!digits:%tail:~0,1%=!" equ "%digits%" goto noDigit
set "head=%head%%tail:~0,1%"
set "tail=%tail:~1%"
goto digit
:noDigit
goto next
:found
for /F "delims=Ee" %%a in ("%tail%") do set "folder=%head%%%a"
exit /B
I accept also Powershell solutions
EDIT: Portion of the file name that I need is that before of S#E##, #x## .#x##, .#x#, .#x## and similar

I would probably use the following script (please consult all the explanatory rem remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~1" & rem /* (target directory; `.` is current working directory, `%~dp0.` is
rem parent of this script, `%~1` is first command line argument) */
set _MASKS="*.mkv" & rem // (space-separated list of quoted file patterns)
set _SEPS=" " "." & rem // (space-separated list of quoted separators)
rem /* Specify multiple `findstr` search strings, including the prefix `/C:`, as you would
rem directly state them at the `findstr` command, which are used to match the particular
rem sub-strings of the file names that are used to find the part where to split them at
rem and to derive the name of the sub-directory where to move the respective file to: */
set _FILTERS=/C:"^S[0123456789][0123456789]*E[0123456789][0123456789]*$" ^
/C:"^[0123456789][0123456789]*x[0123456789][0123456789]*$"
rem // Change into root directory:
pushd "%_ROOT%" && (
rem // Loop through all matching files:
for /F "delims= eol=|" %%F in ('dir /B /A:-D-H-S %%_MASKS%%') do (
rem // Store current file name and extension, initialise some auxiliary variables:
set "NAME=%%~nF" & set "EXT=%%~xF" & set "SDIR= " & set "FLAG=#"
rem // Toggle delayed expansion to avoid trouble with `!` (also later on):
setlocal EnableDelayedExpansion
rem // Replace all predefined separators by spaces:
for %%S in (!_SEPS!) do set "NAME=!NAME:%%~S= !"
rem // Loop through all space-separated (quoted) items of the file name:
for %%I in ("!NAME: =" "!") do (
rem // Skip the loop body when a sub-string has already been found before:
if defined FLAG (
rem // Store current portion of the file name:
endlocal & set "ITEM=%%~I"
rem // Use `findstr` to match against the predefined sub-strings:
cmd /V /C echo(!ITEM!| > nul findstr /R /I %_FILTERS% && (
rem // Match encountered, hence skip this and the remaining items:
set "FLAG="
) || (
rem /* No match found, so append the current item to the name of the
rem sub-directory where the file is supposed to be moved then: */
setlocal EnableDelayedExpansion
for /F "delims=" %%E in ("!SDIR!!ITEM! ") do (
endlocal & set "SDIR=%%E"
)
)
setlocal EnableDelayedExpansion
)
)
rem // Process only file naes where sub-directory names could be derived from:
if not defined FLAG if not "!SDIR:~1,-1!"=="" (
rem // Create sub-directory, if not yet existing:
ECHO 2> nul mkdir "!SDIR:~1,-1!"
rem // Move current file into the sub-directory (but do not overwrite in case):
ECHO if not exist "!SDIR:~1,-1!\!NAME!!EXT!" > nul move "!NAME!!EXT!" "!SDIR:~1,-1!\"
)
endlocal
)
popd
)
endlocal
exit /B
Supposing the script is called consolidate.bat and the target directory is %UserProfile%\Downloads, call the script like this:
consolidate.bat "%UserProfile%\Downloads"
After having tested for the correct output, remove the upper-case ECHO commands in front of the mkdir and move commands!

Assuming we can split the file name on the Digits in the name, this should do the needful.
I put a pause in, inspect the output and make sure that it is giving you the right info before clicking enter.
#(SETLOCAL EnableDelayedExpansion
ECHO OFF
REM SET "_SrcFolder=C:\Users\Administrator\Downloads\test1"
REM SET "_DstFolder=C:\Users\Administrator\Downloads\test2"
REM SET "FileGlob=*.mkv"
SET "_SrcFolder=C:\Admin"
SET "_DstFolder=C:\Admin\test2"
SET "FileGlob=*.txt"
)
CALL :Main
( ENDLOCAL
EXIT /B
)
:Main
FOR %%A IN (
"%_SrcFolder%\%FileGlob%"
) DO (
CALL :Process "%%~nA" "%%~xA" "%%~fA"
)
GOTO :EOF
:Process
SET "_OLDName=%~1"
FOR /F "Tokens=1 Delims=0123456789" %%a IN ("%~1") DO (
SET "_NewFolder=%%a"
)
SET "_NewName=!_OLDName:%_NewFolder%=!"
ECHO._OLDName=%_OLDName%.%~2
ECHO._NewFolder=%_NewFolder%
ECHO._NewName=%_NewName%.%~2
REM pause
ECHO.
ECHO. We will now do the following if you press any key:
ECHO. MD "%_DstFolder%\%_NewFolder%\"
ECHO. COPY "%~3" "%_DstFolder%\%_NewFolder%\%_NewName%%~2"
ECHO.
PAUSE
MD "%_DstFolder%\%_NewFolder%\"
COPY "%~3" "%_DstFolder%\%_NewFolder%\%_NewName%%~2"
GOTO :EOF

Related

Windows Batch Select All files in folder with highest versionnumber

I'm trying to write a windows batch that reads files in a Release folder with multiple Versions for a program.
I have a Release folder like that:
Program01
1.0.0
ivy.xml
1.0.1
ivy.xml
Program02
1.0.0
ivy.xml
2.0.0
ivy.xml
I need a windows batch that only reads in the ivy.xml for each program with the highest versionnumber. So for the example above it should read:
Program01/1.0.1/ivy.xml
Program02/2.0.0/ivy.xml
I only need the code that selects the ivy.xml in the newest folder.
Try this one:
#echo off
set RootFolder=Test
pushd "%RootFolder%"
For /d %%a in (*) do call :SetVersion "%%~a"
echo.
pause
:SetVersion
for /f "delims=" %%b in ('dir /b /ad /o-n "%~1"') do (
if /i exist "%~1\%%b\ivy.xml" echo %~1\%%b\ivy.xml
goto :EOF
)
If the version number folders always consist of three numerals separated by a dot, the following simple approach should do what you want:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=Release" & rem // (path to root directory)
set "_SDIR=Program*" & rem // (pattern for first-level sub-directories)
set "_MASK=*.*.*" & rem // (pattern for second-level sub-sirectories)
set "_FILE=ivy.xml" & rem // (name of particular file to be found)
rem // Iterate through the immediate sub-directories of the given root directory:
for /D %%J in ("%_ROOT%\%_SDIR%") do (
rem // Loop through the sub-sub-directories sorted in alphabetic order:
for /F "delims= eol=|" %%I in ('dir /B /A:D /O:N "%%~J\%_MASK%"') do (
rem // Ensure there is a specific file but not a sub-directory:
if exist "%%~J\%%I\%_FILE%" if not exist "%%~J\%%I\%_FILE%\*" (
rem /* Store currently iterated item; since this value becomes overwritten during
rem each iteration, the eventual value constitutes the highest version number: */
set "LATEST=%%~J\%%I\%_FILE%"
)
)
rem // Return the latest version directory per each sub-directory:
call echo/%%LATEST%%
)
endlocal
exit /B
If version number components may consist of multiple figures (like 2.13.685), it becomes more complicated because you must implement alphanumeric sorting yourself because standard alphabetic sort would return false results (since 10 comes before 2 then):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=Release" & rem // (path to root directory)
set "_SDIR=Program*" & rem // (pattern for first-level sub-directories)
set "_MASK=*.*.*" & rem // (pattern for second-level sub-sirectories)
set "_FILE=ivy.xml" & rem // (name of particular file to be found)
set "_TMPF=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (path to temporary file)
rem // Iterate through the immediate sub-directories of the given root directory:
for /D %%J in ("%_ROOT%\%_SDIR%") do (
rem // Open a temporary file for writing:
> "%_TMPF%" (
rem // Loop through sub-sub-directories:
for /D %%I in ("%%~J\%_MASK%") do (
rem // Ensure there is a specific file but not a sub-directory:
if exist "%%~I\%_FILE%" if not exist "%%~I\%_FILE%\*" (
rem // Store currently iterated item:
set "ITEM=%%~I\%_FILE%"
rem // Split the version number into sot-separated portions:
for /F "tokens=1-3* delims=. eol=." %%A in ("%%~nxI") do (
rem // Precede these portions with zeros:
set "AA=0000%%A" & set "BB=0000%%B" & set "CC=0000%%C" & set "DD=0000%%D"
rem /* Write the portions padded with zeros to the left plus a `|`
rem plus the original name of the currently iterated item; the
rem part left to the `|` is just needed for sorting; due to the
rem padding alphabetic sorting equals alphanumeric sorting: */
setlocal EnableDelayedExpansion
echo(!AA:~-4!.!BB:~-4!.!CC:~-4!.!DD:~-4!^|!ITEM!
endlocal
)
)
)
)
rem // Read from the temporary file and iterate through alphabetically sorted lines:
for /F "tokens=2 delims=| eol=|" %%I in ('sort "%_TMPF%"') do (
rem /* Store the part right to the `|` of the currently iterated item,
rem which constitutes its original name; the variable eventually
rem contains the highest version number: */
set "LAST=%%I"
)
rem // Return the latest version directory of the current sub-directory:
setlocal EnableDelayedExpansion
echo(!LAST!
endlocal
)
rem // Clean up temporary file:
del "%_TMPF%"
endlocal
exit /B

Move files into a subfolder of a destination directory

I started using batch commands last week and I've reached a real obstacle with a script I made.
What I want to do
Move a PDF file from C:\Users\JK\Documents\reports PDFs into pre-made subfolders in the destination W:\Departments\QA\cases.
For example the script would move 2223 report.pdf to W:\Departments\QA\cases\2201 - 2300\2223
What I tried
I made a script based off the answer in this thread
cls
#pushd %~dp0
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\Users\JK\Documents\reports PDFs"
set "DestDir=W:\Departments\QA\cases\"
for /F "eol=| delims=" %%A in ('dir /B /A-D-H "%SourceDir%\*.pdf" 2^>nul') do (
for /F "eol=| tokens=1" %%B in ("%%~nA") do (
for /D %%C in ("%DestDir%\%%B*") do move /Y "%SourceDir%\%%A" "%%C\"
)
)
endlocal
popd
pause
Where I am stuck
How could I add subfolders or account for them in the destination directory?
FYI, I also tried adding a wildcard symbol at the end of the destination directory by changing %DestDir%\%%B to %DestDir%\*\%%B*.
I would probably accomplish the task with the following script (see all the explanatory rem remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=C:\Users\JK\Documents\reports PDFs" & rem // (source directory; `.` or `` is current, `%~dp0.` is script parent)
set "_TARGET=W:\Departments\QA\cases" & rem // (target directory; `.` or `` is current, `%~dp0.` is script parent)
set "_SUBDIR=#" & rem // (set to non-blank value in order to create individual sub-directories)
set "_FMASK=????*.pdf" & rem // (pattern to match source files)
set "_DMASK=???? - ????*" & rem // (pattern to match target directories)
set "_FFIL1=^[0123456789][0123456789][0123456789][0123456789] .*\.pdf$" & rem // (filter 1 for source files)
set "_FFIL2=^[0123456789][0123456789][0123456789][0123456789]\.pdf$" & rem // (filter 2 for source files)
set "_DFIL1=^[0123456789][0123456789][0123456789][0123456789] - [0123456789][0123456789][0123456789][0123456789] .*$"
set "_DFIL2=^[0123456789][0123456789][0123456789][0123456789] - [0123456789][0123456789][0123456789][0123456789]$"
rem // Change into source directory:
pushd "%_SOURCE%" && (
rem // Iterate through all files matching the specified pattern and filters:
for /F "eol=| delims=" %%F in ('
dir /B /A:-D-H-S "%_FMASK%" ^| findstr /I /R /C:"%_FFIL1%" /C:"%_FFIL2%"
') do (
rem // Store full path of currently iterated file:
set "FILE=%%~fF"
rem // Extract the leading numeric part of the file name:
for /F "eol=| delims= " %%N in ("%%~nF") do (
rem // Store the numeric part:
set "NUM=%%N"
rem /* Remove any leading zeros from the numeric part of the file name, because
rem such cause the number to be unintentionally interpreted as octal: */
set /A "INT=0" & for /F "eol=| tokens=* delims=0" %%Z in ("%%N") do 2> nul set /A "INT=%%Z"
)
rem // Change into target directory:
pushd "%_TARGET%" && (
rem // Iterate through all directories matching the specified pattern and filters:
for /F "eol=| delims=" %%D in ('
cmd /V /C 2^> nul dir /B /A:D-H-S "!_DMASK:|=%%I!" ^| findstr /I /R /C:"%_DFIL1%" /C:"%_DFIL2%"
') do (
rem // Store name of currently iterated directory:
set "TDIR=%%D"
rem // Extract first (from) and second (to) numeric parts of directory names:
for /F "eol=| tokens=1-2 delims=- " %%S in ("%%D") do (
rem // Remove any leading zeros from first (from) numeric part:
set /A "FRM=0" & for /F "eol=| tokens=* delims=0" %%Z in ("%%S") do set /A "FRM=%%Z"
rem // Remove any leading zeros from second (to) numeric part:
set /A "TOO=0" & for /F "eol=| tokens=* delims=0" %%Z in ("%%T") do set /A "TOO=%%Z"
)
rem // Toggle delayed variable expansion to avoid trouble with `!`:
setlocal EnableDelayedExpansion
rem /* Do integer comparisons to check whether the numeric part of the file name
rem lies within the range given by the directory name (from/to): */
if !INT! geq !FRM! if !INT! leq !TOO! (
rem // Numeric part of file name lies within range, hence try to move file:
if defined _SUBDIR (
2> nul md "!TDIR!\!NUM!"
ECHO move "!FILE!" "!TDIR!\!NUM!\"
) else (
ECHO move "!FILE!" "!TDIR!\"
)
)
endlocal
)
rem // Return from target directory:
popd
)
)
rem // Return from source directory:
popd
)
endlocal
exit /B
You may need to adapt a few constants to your situation in the section commented with Define constants here: at the top.
After having tested the script for the correct output, remove the upper-case ECHO commands in front of the move commands! In order to avoid multiple 1 file(s) moved. messages, replace these ECHO commands by > nul.

Windows batch script to renumber files in folder

I have a folder with files that are (ideally) sequentially named. But sometimes I want to add a new file into the sequence, which I do by appending a letter, so that it still sorts in the right order, e.g.
I want a batch that renames these back into a proper sequence, i.e. P01.svg, P02.svg, P03.svg, etc. Of course, the correct order must be preserved in the process.
I've tried various things, but can't find a solution that preserves the order... sometimes the renaming appears to be done in the wrong order so that the files get out of sequence. My latest attempt is:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
REM first rename with xxx prefix to avoid name clashes, then do a second loop to remove the xxx prefix
set /a i=1
for /f "delims=" %%a in ('dir *.svg /b /a-d-h-s') do (
set "p=0!i!"
ren "%%a" "xxxP!p:~-2!.svg"
set /a i = i + 1
)
REM second loop...
set /a i=1
for /f "delims=" %%a in ('dir *.svg /b /a-d-h-s') do (
set "p=0!i!"
ren "%%a" "P!p:~-2!.svg"
set /a i = i + 1
)
Why are the renamed files not in the correct order every time?
You don't need you first loop. A simple ren command is sufficient.
For the correct order (by name), just expand the dircommand with the /on option ("Order by Name")
#echo off
setlocal enabledelayedexpansion
ren *.svg *.svg.tmp
set nr=100
for /f "delims=" %%a in ('dir /b /a-d-h-s /on *.svg.tmp') do (
set /a nr+=1
ECHO ren "%%a" "P!nr:~-2!.svg"
)
Note: I disabled the ren command for security reasons. When the output fits your needs, just remove the ECHO
You do not need to use two loops and neither do you have to rename the files twice. If you simply count the number of files in advance and then rename then in descending order (hence from highest to lowest) there should not be any collisions possible, given that the files have got consecutive numbers (no gaps allowed).
Here is a script that does exactly this (see all the explanatory rem remarks in the code):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=" & rem /* (root directory; empty or `.` is current,
rem `%~dp0.` is location of this script) */
set "_PREF=P" & rem // (desired file name prefix)
set "_MASK=%_PREF%*.svg" & rem // (search pattern for files)
set /A "_DIGS=2" & rem // (number of digits in the new names)
rem // Get limit of number of files that can be handled:
set /A "CNT=1" & for /L %%D in (1,1,%_DIGS%) do set /A "CNT*=10"
rem // Change into root directory:
pushd "%_ROOT%" && (
rem // Count number of matching files, quit if there are too many:
for /F %%C in ('dir /B /A:-D-H-S "P*.svg" ^| find /C /V ""') do (
set /A "CNT+=%%C" & if %%C geq %CNT% exit /B 2
)
rem /* List files sorted in descending order by name and prepend
rem with an ascending index number: */
for /F "tokens=1* delims=:" %%E in ('
dir /B /A:-D-H-S /O:-N "P*.svg" ^| findstr /N "^"
') do (
rem /* Split off the index number and determine the new number
rem to be used in the new file name: */
set /A "FNUM=CNT-%%E+1"
rem // Store the current file base name and extension:
set "FILE=%%~nF" & set "FEXT=%%~xF"
setlocal EnableDelayedExpansion
rem // Actually rename the file:
ECHO ren "!FILE!!FEXT!" "!_PREF!!FNUM:~-%_DIGS%!!FEXT!"
endlocal
)
popd
)
endlocal
exit /B
After having tested for the correct output, remove the upper-case ECHO command!
This is an approved variant that can even handle collisions (meaning a file with a new name already exists), which may occur when there are gaps in the original (numeric) sequence of file names; in such cases, an additional suffix .tmp is temporarily appended and after having processed all files that suffix becomes removed by a single ren command:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=" & rem /* (root directory; empty or `.` is current,
rem `%~dp0.` is location of this script) */
set "_PREF=P" & rem // (desired file name prefix)
set "_MASK=%_PREF%*.svg" & rem // (search pattern for files)
set "_SUFF=.tmp" & rem // (temporary suffix to handle collisions)
set /A "_DIGS=2" & rem // (number of digits in the new names)
rem // Get limit of number of files that can be handled:
set /A "CNT=1" & for /L %%D in (1,1,%_DIGS%) do set /A "CNT*=10"
rem // Change into root directory:
pushd "%_ROOT%" && (
rem // Terminate if there are files with the temporary suffix:
if defined _SUFF if exist "%_MASK%%_SUFF%" popd & exit /B 3
rem // Count number of matching files, quit if there are too many:
for /F %%C in ('dir /B /A:-D-H-S "P*.svg" ^| find /C /V ""') do (
set /A "CNT+=%%C" & if %%C geq %CNT% popd & exit /B 2
)
rem /* List files sorted in descending order by name and prepend
rem with an ascending index number: */
for /F "tokens=1* delims=:" %%E in ('
dir /B /A:-D-H-S /O:-N "P*.svg" ^| findstr /N "^"
') do (
rem /* Split off the index number and determine the new number
rem to be used in the new file name: */
set /A "FNUM=CNT-%%E+1"
rem // Store the current file base name and extension:
set "FILE=%%~nF" & set "EXTF=%%~xF" & set "EXTT="
setlocal EnableDelayedExpansion
rem /* Actually rename the file; in case of collisions, append
rem another suffix to the name and rename again later: */
set "NAME=!_PREF!!FNUM:~-%_DIGS%!!EXTF!"
if /I not "!FILE!!EXTF!"=="!NAME!" (
if exist "!NAME!" set "EXTT=!_SUFF!"
ECHO ren "!FILE!!EXTF!" "!NAME!!EXTT!"
)
endlocal
)
rem // Resolve potential collisions, so remove additional suffixes:
setlocal EnableDelayedExpansion
if defined _SUFF if exist "!_MASK!!_SUFF!" ren "!_MASK!!_SUFF!" "*."
endlocal & popd
)
endlocal
exit /B
Again remove the upper-case ECHO command to actually rename files!

Batch Script - trying to replace a text string in all files of a specific extension

I am trying to change all *.gpx files in a directory, editing out all instances of "Flag, Blue" with "Waypoint" (without quotation marks). I'm not great at Windows script and so want a little help debugging.
I have based this code on code by in question:
Batch Script - Find and replace text in multiple files in a directory without asking user to install any program or add other files to my batch script
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // based on code by aschipfl
rem // https://stackoverflow.com/questions/46467475/batch-script-find-and-replace-text-in-multiple-files-in-a-directory-without-as
rem // Define constants here:
set "_MASK=*.gpx" & rem // (working on all GPX files)
set "_SEARCH=Flag, Blue" & rem // (find those HORRIBLE blue flags)
set "_REPLAC=Waypoint" & rem // (repace with WAYPOINTS)
set "FOROPT=" & rem // NON-recursive
set "IFSW=" & rem // CaSe sEnSiTiVe YeS
set "_TMPF=%TEMP%\%~n0_%RANDOM%.tmp" & rem // (path to temporary file)
pushd "." || exit /B 1
rem // Loop through all matching files in the directory tree:
for %FOROPT% %%F in ("%_MASK%") do (
rem // Write to temporary file:
> "%_TMPF%" (
rem /* Read current file line by line; use `findstr` to precede every line by
rem its line number and a colon `:`; this way empty lines appear non-empty
rem to `for /F`, which avoids them to be ignored; otherwise empty lines
rem became lost: */
for /F "delims=" %%L in ('findstr /N "^" "%%~fF"') do (
rem // Store current line text:
set "LINE=%%L" & set "FLAG="
setlocal EnableDelayedExpansion
rem // Remove line number prefix:
set "LINE=!LINE:*:=!"
rem // Skip replacement for empty line text:
if defined LINE (
rem /* Use `for /F` loop to avoid trouble in case search or replace
rem strings contain quotation marks `"`: */
for /F "tokens=1* delims== eol==" %%I in ("!_SEARCH!=!_REPLAC!") do (
rem // Query to handle case-sensitivity:
if %IFSW% "!LINE!"=="!LINE:%%I=%%I!" (
rem // Detect whether replacement changes line:
if not "!LINE!"=="!LINE:%%I=%%J!" (
rem // Actually do the sub-string replacement:
set "LINE=!LINE:%%I=%%J!"
set "FLAG=#"
)
)
)
)
rem // Output the resulting line text:
echo(!LINE!
if defined FLAG (endlocal & set "FLAG=#") else (endlocal)
)
)
rem // Check whether file content would change upon replacement:
if defined FLAG (
rem // Move the temporary file onto the original one:
> nul move /Y "%_TMPF%" "%%~fF"
) else (
rem // Simply delete temporary file:
del "%_TMPF%"
)
)
popd
endlocal
exit /B
I run the script but no changes to the GPX files.
A real-world example segment from the GPX file would be:
<ele>1.19734255318821</ele>
<time>2019-07-28T00:42:12Z</time>
<name>CW1002</name>
<sym>Flag, Blue</sym>
<extensions>
<trp:ViaPoint>
Obviously I want this to remain the same except:
<sym>Waypoint</sym>

Windows batch code to merge file and edit

I have a lot of csv file into a folder.
The files are named like OPERATORS_*.csv where * is a variable.
I want, using a batch file, to merge all files into one, delete the first row of each file and add at the end of each row the *.
I have tried this code:
copy /b OPERATORS_*.csv OPERATORS_FULL.csv
This way is fine, but the first row of each file is printed and i lost the attribute in the filename.
Example:
OPERATORS_ACTIVITY1.csv
OPT;SALES;REDEMPTION
OPT1;12;75
OPERATORS_ACTIVITY2.csv
OPT;SALES;REDEMPTION
OPT2;22;64
and i want this:
OPERATORS_FULL.csv
OPT1;12;75;ACTIVITY1
OPT2;22;64;ACTIVITY2
Any suggestions?
Try this (Update #2):
#ECHO OFF
SETLOCAL EnableDelayedExpansion
IF EXIST OPERATORS_FULL.csv DEL OPERATORS_FULL.csv
IF EXIST OPERATORS_FULL.tmp DEL OPERATORS_FULL.tmp
FOR %%A IN ( OPERATORS_*.csv ) DO (
:: get attribute from filename
SET "attr=%%A"
SET "attr=!attr:OPERATORS_=!"
SET "attr=!attr:.csv=!"
:: get date suffix
SET tmp=!attr:_= !
FOR %%G IN ( !tmp! ) DO (
SET date_=%%G
)
:: if we have a date (i.e. a numeric value)
IF !date_! EQU +!date_! (
:: ...remove date from attr with leading underscore
CALL SET attr=%%attr:_!date_!=%%
) ELSE (
:: ...else clear date variable
SET date_=
)
:: dump CSVs, skipping each header line, adding the attribute from the filename
FOR /F "skip=1 tokens=*" %%G IN ( %%A ) DO ECHO %%G;!attr!;!date_! >> OPERATORS_FULL.tmp
)
REN OPERATORS_FULL.tmp OPERATORS_FULL.csv
Here is a different approach using redirection -- see all the explanatory rem remarks in the script:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_INPUT=OPERATORS_*.csv" & rem // (input files)
set "_OUTPUT=OPERATORS_FULL.csv" & rem // (output file)
set /A "_SKIP=1" & rem // (number of lines to skip for each input file)
rem // Redirect the whole output at once:
> "%_OUTPUT%" (
rem // Iterate over all the input files:
for %%F in ("%_INPUT%") do (
rem // Store the current file name to get the attribute name later:
set "NAME=%%~nF"
rem // Exclude the output file from being processed:
if /I not "%%~nxF"=="%_OUTPUT%" (
rem // Determine the number of lines of the current input file:
for /F %%E in ('^< "%%~F" find /C /V ""') do set /A "CNT=%%E"
rem // Read current input file:
< "%%~F" (
setlocal EnableDelayedExpansion
rem // Loop over every line:
for /L %%E in (1,1,!CNT!) do (
rem // Read current line:
set "LINE=" & set /P LINE=""
rem // Return current line if it is not to be skipped:
if %%E GTR %_SKIP% echo(!LINE!;!NAME:*_=!
)
endlocal
)
)
)
)
endlocal
exit /B
#echo off
setlocal
del operators_full.csv 2>nul >nul
FOR %%f IN (operators_*.csv) DO for /f "usebackqdelims=" %%a in ("%%f") do echo %%a>operators_full.txt&goto body
:body
(
FOR %%f IN (operators_*.csv) DO FOR /f "tokens=1*delims=_" %%s IN ("%%~nf") DO for /f "skip=1usebackqdelims=" %%a in ("%%f") do echo %%a;%%t
)>>operators_full.txt
move operators_full.txt operators_full.csv
First, delete the output file if it exists, then start copying the file(s) to a .txt file but deliberately abort after the very first line.
then, for each file, tokenise on the _ in the name part of the file %%f copy every line,appending the post-_ part of the filename in %%t, skipping the first and append to the .txt file (note the position of the outer pair of parentheses - this syntax allows the output of the entire code block to be redirected)
Finally, move or rename the file.
Oh -- you don't want the header line? Omit the first for line.

Resources