Move files into a subfolder of a destination directory - windows

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.

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

Get a variable from filename string in windows batch

good day, i have a folder with 400 files and i want to print a "name" from the filenames
this is the structure
ej:
20201323223_vendo.perfil01_17872513294967257_1601950878_live.mp4
20201323223__vvcastrillon_12_17949951031375250_1601939874_live.mp4
2020323123_yessromero.g_17849208194340047_1601945592_live.mp4
2020323223_ziizii_08_17979840166310477_1601929868_live.mp4
and what i need is
vendo.perfil01
_vvcastrillon_12
yessromero.g
ziizii_08
Im try to loop in the files and separate whit the _ and extract the 2 and 3 token numeral conditioning but the result is wrong and missing variables
#echo off
setlocal EnableDelayedExpansion
:loop
SET max=5000
for /F "delims=" %%I in ('dir "*_*_*.mp4" /A-D /B /S 2^>nul ^| %SystemRoot%\System32\findstr.exe /I /L /V "\\outdir\\"') do (for /F "eol=| tokens=2,3 delims=_" %%J in ("%%~nI") do (SET "var="&for /f "delims=0123456789" %%a in ("%%K") do SET var=%%a
if defined var ( set nam=%%J_%%K ) else ( set nam=%%J )
)
echo/!nam!
)
timeout 10 > nul
goto loop
i think the answer is remove the first number before the _ then the string _xxxxxx_xxxxxxx_live.mp4 at the end but i dont know how read in reverse the tokens
tanks for any help
Since you have got different numbers of _-separated items in your file names and even adjacent _, so using for /F to split them into specific tokens with delims=_ is not the best choice.
I would use a standard for-loop instead, which receives modified file names, namely with each _ replaced by " " and enclosed within "", which leads to SPACE-separated partial name items. So:
20201323223_vendo.perfil01_17872513294967257_1601950878_live.mp4
is changed to:
"20201323223" "vendo.perfil01" "17872513294967257" "1601950878" "live.mp4"
before looping. Within the loop implement an index counter and just append those items to a buffer whose index numbers lie within a certain range that depends on the total number of items.
Here is an example script that demonstrates what I mean:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (full path to target directory)
set "_MASK=*_*_*_*_live.mp4" & rem // (pattern to match file names)
set "_FILT=^[0123456789][0123456789]*_..*_[0123456789][0123456789]*_[0123456789][0123456789]*_live\.mp4$"
rem // (additional `findstr` filter for file names)
set /A "_POS=1" & rem /* (index of first `_`-separated item to extract;
rem `0` is the first item, `-1` is the last) */
set /A "_NUM=-4" & rem /* (number of `_`-separated items to extract;
rem `-1` means all up to the last item) */
rem // Change into target directory:
pushd "%_ROOT%" && (
rem // Loop through all matching non-hidden, non-system files:
for /F "delims= eol=|" %%F in ('
dir /B /A:-D-H-S "%_MASK%" ^| findstr /I /R /C:"%_FILT%"
') do (
rem // Store current file name, initialise item index, counter and buffer:
set "FILE=%%F" & set /A "IDX=-1, CNT=-1" & set "BUFF=_"
rem // Toggle delayed expansion to avoid troubles with `!`:
setlocal EnableDelayedExpansion
rem // Count number of items and store one less:
rem set "TEST=%FILE:_=" & set /A "CNT+=1" & set "TEST=%"
for %%I in ("!FILE:_=" "!") do set /A "CNT+=1"
rem // Determine item index position from given index and number:
if !_POS! lss 0 (set /A "BEG=CNT+_POS+1") else set /A "BEG=_POS"
if !_NUM! lss 0 (set /A "END=CNT+_NUM+1") else set /A "END=_POS+_NUM-1"
rem // Transport numbers over `endlocal` barrier:
for %%C in (!CNT!) do for %%B in (!BEG!) do for %%A in (!END!) do (
rem // Loop through `_`-separated items of file name:
for %%I in ("!FILE:_=" "!") do (
rem // Store current item, increment item index:
endlocal & set "ITEM=%%~I" & set /A "IDX+=1"
setlocal EnableDelayedExpansion
rem // Append current item to buffer if in range:
if !IDX! geq %%B if !IDX! leq %%A (
rem // Transport buffer over `endlocal` barrier:
for /F "delims=" %%E in ("BUFF=!BUFF!_!ITEM!") do (
endlocal & set "%%E"
setlocal EnableDelayedExpansion
)
)
)
)
rem // Return buffer:
echo(!BUFF:~2!
endlocal
)
rem // Return from target directory:
popd
)
endlocal
exit /B
Something like this should help:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1* delims=_." %%i in ('dir /b /s /a-d "*_*_*.mp4"2^>nul ^| findstr.exe /ILV "\\outdir\\"') do (
set "var=%%j"
for /f "tokens=2,* delims=_." %%a in ("%%j") do echo !var:_%%b=!
)
Keep in mind that using delims will also split on consecutive characters like double underscore. for those you need to predetermine which has double underscore and let the script add it for you.
#ECHO OFF
SETLOCAL
for %%a in (
20201323223_vendo.perfil01_17872513294967257_1601950878_live.mp4
20201323223__vvcastrillon_12_17949951031375250_1601939874_live.mp4
2020323123_yessromero.g_17849208194340047_1601945592_live.mp4
2020323223_ziizii_08_17979840166310477_1601929868_live.mp4
) do set "filename=%%a" &call :process&echo --------------------------------------
GOTO :EOF
:process
echo stage 1 %filename%
:: step 1 : delete all characters up to and including the first underscore
set "filename=%filename:*_=%"
echo stage 2 %filename%
:: step 2 : find all numeric strings of length 4 or more in remainder
call :strsgt4 %filename:_= %
:: step 3 : replace each numeric string of length 4 or more + preceding underscore with "/" (invalid filename character)
echo stage 3 %filename%
:proc3lp
if "%zapstrings%" neq " " for %%v in (%zapstrings%) do call set "filename=%%filename:_%%v=/%%"&call set "zapstrings=%%zapstrings: %%v=%%"&goto proc3lp
echo stage 4 %filename%
:: step 5 : Remove all charactersincluding and after the first "/"
for /f "delims=/" %%v in ("%filename%") do echo result %%v
goto :eof
:strsgt4
set "zapstrings= "
:strsgt4loop
set "test=%1"
if not defined test goto :eof
set "test=%test:~4%"
if defined test call :isnum %test%&if not defined notnumber set "zapstrings=%zapstrings% %1"
shift
goto strsgt4loop
:: Determine whether %1 is purely numeric
:isnum
SET "notnumber=9%~1"
FOR /l %%z IN (0,1,9) DO CALL SET "notnumber=%%notnumber:%%z=%%"
GOTO :eof
Really a question of working out what your rules are.
I decided that your rules were: string after the first underscore, until before the first subsequent underscore that precedes a numeric string or length greater than 3
The %%a loop simply submits a sequence of sample strings to :process
The inline comments should explain the remainder.

File/Folder sorting by semi-complex names

I need to sort a series of images into folders by part of their names.
The only thing consistent is a series of numbers after an underscore.
I would like to use the content before the underscore as the folder name.
The problem is that some of the images have an underscore in the name.
I'm just not getting the variables right...
Currently using:
#echo off
setlocal enabledelayedexpansion
for %%A in (*.jpg) do (
echo file found %%A
for /f "delims=" %%B in ("%%A") do set fname=%%~nB
for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
for /f "tokens=1* delims=_" %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! does not exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
Examples of problematic names:
a_t_r_a_v__a_83563614_2994781403891160_6736610473828214002_n.jpg
alexa_vn10s1ty429_81834750_1570208686488587_1442450484794514255_n.jp
More common names:
shurra.m.lance_96141088_342661823370490_6342263806980952485_n.jpg
anu.m0111_104134441_573701159868969_589828829350351913_n.jpg
Currently hoping to get these turned into folders as:
a_t_r_a_v__a
alexa_vn10s1ty429
shurra.m.lance
anu.m0111
Just not getting it to ignore the right underscores.
Thanks for your time in advance. I am learning so much here but I guess I'm just missing the last piece.
This is not a trivial task, but I think I have found a solution (see all the explanatory rem remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem /* Loop through matching files; `findstr` additionally filters them, so only
rem items containing an underscore followed by a decimal digit are processed;
rem exclamation marks are prohibited in the portion behind the prefix: */
for /F "delims= eol=|" %%F in ('
dir /B /A:-D-H-S "*_*.jpg" ^| findstr /RIC:"[^_]_[0123456789][^!]*\.jpg$"
') do (
rem // Store current file name:
set "FILE=%%F" & set "TEST=%%F"
rem // Toggle delayed expansion to avoid problems with exclamation marks:
setlocal EnableDelayedExpansion
rem // Define ascending and descending number sequences for the next loop:
for %%J in ("0,1,9" "9,-1,0") do (
rem // Loop through all decimal digits, increasing, then decreasing:
for /L %%I in (%%~J) do (
rem // Find `_` plus the current digit, remove everything in front:
if not "!TEST:*_%%I=!"=="!TEST!" set "NAME=!TEST:*_%%I=_%%I!"
)
rem /* Remove previously end of file name to get the desired prefix
rem (at this point exclamation marks could cause problems): */
for /F "delims= eol=|" %%E in ("!NAME!") do set "TEST=!TEST:%%E=!"
)
rem // Create desired directory and move current file into it:
ECHO md "!TEST!" 2> nul
ECHO move /Y "!FILE!" "!TEST!\"
endlocal
)
endlocal
exit /B
After having tested for the correct output, remove the upper-case ECHO commands.
This is a simpler (and faster) method:
#echo off
setlocal EnableDelayedExpansion
set "digits=0123456789"
for %%A in (*.jpg) do (
set "fname=%%A"
rem Initialize "first digit after an underscore"
set "firstDigit="
rem Split name at underscores
for %%a in ("!fname:_= " "!") do (
if not defined firstDigit (
set "part=%%~a"
rem Check if first char in part is in "digits"
for /F %%b in ("!part:~0,1!") do if "!digits:%%b=X!" neq "%digits%" set "firstDigit=%%b"
)
)
rem Split name at "underscore+firstDigit"
for /F %%a in ("!firstDigit!") do for /F %%b in ("!fname:_%%a= !") do set "folname=%%b"
echo Moving file %%A to folder !folname!
)
Output example:
Moving file a_t_r_a_v__a_83563614_2994781403891160_6736610473828214002_n.jpg to folder a_t_r_a_v__a
Moving file alexa_vn10s1ty429_81834750_1570208686488587_1442450484794514255_n.jp to folder alexa_vn10s1ty429
Moving file shurra.m.lance_96141088_342661823370490_6342263806980952485_n.jpg to folder shurra.m.lance
Moving file anu.m0111_104134441_573701159868969_589828829350351913_n.jpg to folder anu.m0111

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!

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