Batch: Create folders from filename (substring) - windows

i have loads of files which i want to organize differently. The batch script should create folders with the substring on the left side of the date in the filename.
Files are now named like this:
This_is_my_file_21.01.29_22-00_abc_115.avi
This_is_my_file_20.09.29_21-10_abc_15.avi
This_is_another_file_21.01.29_22-00_abc_55.avi
Pattern:
<Name with unknown number of underscores>_<YY.MM.DD>_<hh-mm>_<string with unknown length>_<number n from 1-999>.avi
Folders should be named like this:
This_is_my_file <- two files will go into this directory
This_is_another_file <- only one file.
The Problem is, how do I get the correct substring for my folder name?
This is what I have so far:
#echo off
setlocal
set "basename=."
for /F "tokens=1* delims=." %%a in ('dir *.avi /B /A-D ^| sort /R') do (
set "filename=%%a"
setlocal EnableDelayedExpansion
for /F "delims=" %%c in ("!basename!") do if "!filename:%%c=!" equ "!filename!" (
set "basename=!filename!"
md "!basename:~0,-23!"
)
move "!filename!.%%b" "!basename:~0,-23!"
for /F "delims=" %%c in ("!basename!") do (
endlocal
set "basename=%%c
)
)

#ECHO OFF
SETLOCAL
rem The following settings for the source directory, destination directory, target directory,
rem batch directory, filenames, output filename and temporary filename [if shown] are names
rem that I use for testing and deliberately include names which include 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 /f "delims=" %%b IN ('dir /b /a-d "%sourcedir%\*.avi" ' ) DO (
SETLOCAL ENABLEDELAYEDEXPANSION
CALL :countus "%%b"
IF DEFINED subdir (
MD "!subdir!" 2>NUL
ECHO MOVE "%sourcedir%\%%b" "%sourcedir%\!subdir!\"
) ELSE (
ECHO Failed pattern check %%b
)
ENDLOCAL
)
GOTO :EOF
:: count number of underscores before pattern YY.MM.DD_hh-mm
:countus
SET /a ucount=0
:countusloop
SET /a ucount+=1
SET /a scount=ucount+1
FOR /f "tokens=%ucount%,%scount%delims=_" %%q IN ("%~1") DO SET "str1=%%q"&SET "str2=%%r"
IF NOT DEFINED str2 SET "subdir="&GOTO :EOF
:: is %str1%.%str2:-=.%. of form np.np.np.np.np where np is a number-pair?
SET "candidate=%str1%.%str2:-=.%."
FOR /L %%c IN (10,1,99) DO IF DEFINED candidate SET "candidate=!candidate:%%c.=!"&IF NOT DEFINED candidate GOTO success
FOR /L %%c IN (0,1,9) DO IF DEFINED candidate SET "candidate=!candidate:0%%c.=!"&IF NOT DEFINED candidate GOTO success
GOTO countusloop
:success
SET "subdir=%~1"
FOR /f "delims=:" %%e IN ("!subdir:_%str1%_%str2%=:!") DO SET "subdir=%%e"
GOTO :eof
The "move" command is merely echoed for verification. Remove the echo from echo move to actually move the files.

This possible solution uses the fact that your filenames have a known number of underscores if you work backwards. All I do is replace those underscores with backslashes, which obviously cannot already be contained in the filename. I can then use the relative paths to step up the filename, as if it were a directory tree, until all I have left is the part ahead of the date sequence, which I then replace the backslashes with underscores again. I use the result of that with robocopy, which has a move option, and will create the destination directory automatically, if it does not already exist. At the outset, I perform the directory search, in the same directory as the batch-file, using where.exe, (you can change that, on line three, from "%~dp0." to ".", if you want to use the current directory instead, or "any other path" as necessary). where.exe not only treats the ? wildcard as exactly one character, (unlike the dir command which is zero or one), but also ignores 8.3 naming. It therefore treats the .avi extension exactly as written, (and not 'beginning with' .avi, which dir, or a standard for loop, would).
Anyhow, feel free to give it a try:
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "}=" & For /F Delims^= %%G In ('(Set PATHEXT^=^) ^& %__AppDir__%where.exe
"%~dp0.":"?*_??.??.??_??-??_?*.avi" 2^> NUL') Do (Set "}=%%~nG"
SetLocal EnableDelayedExpansion & For %%H In ("\!}:_=\!") Do (
EndLocal & For %%I In ("%%~pH..\..") Do (Set "}=%%~pI"
SetLocal EnableDelayedExpansion & Set "}=!}:~1,-1!"
For %%J In ("!}:\=_!") Do (EndLocal & %__AppDir__%robocopy.exe ^
"%%~dpG." "%%~dpG%%~J" "%%~nxG" /Mov 1> NUL))))
If you want even further robustness, and do not wish to use a more suitable scripting technology, the following, extremely complex looking, version, is the same code, except that it uses findstr to validate the date and time sequence. It filters those avi files containing the following pattern, _yy.MM.dd_hh-mm_ in the avi filenames, using all dates from the beginning of 1970 up until the end of 2021:
#Echo Off & SetLocal EnableExtensions DisableDelayedExpansion
Set "}=" & For /F Delims^= %%G In ('(Set PATHEXT^=^) ^& %__AppDir__%where.exe
"%~dp0.":"?*_??.??.??_??-??_?*.avi" 2^> NUL ^| %__AppDir__%findstr.exe
/RC:"_[789][0123456789].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[789][0123456789].1[012].3[01]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_[01][0123456789].1[012].3[01]_2[0123]-[012345][0123456789]_"
/C:"_2[01].0[123456789].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].0[123456789].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].0[123456789].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].0[123456789].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].0[123456789].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].0[123456789].3[01]_2[0123]-[012345][0123456789]_"
/C:"_2[01].1[012].0[123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].1[012].0[123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].1[012].[12][0123456789]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].1[012].[12][0123456789]_2[0123]-[012345][0123456789]_"
/C:"_2[01].1[012].3[01]_[01][0123456789]-[012345][0123456789]_"
/C:"_2[01].1[012].3[01]_2[0123]-[012345][0123456789]_"') Do (Set "}=%%~nG"
SetLocal EnableDelayedExpansion & For %%H In ("\!}:_=\!") Do (
EndLocal & For %%I In ("%%~pH..\..") Do (Set "}=%%~pI"
SetLocal EnableDelayedExpansion & Set "}=!}:~1,-1!"
For %%J In ("!}:\=_!") Do (EndLocal & %__AppDir__%robocopy.exe ^
"%%~dpG." "%%~dpG%%~J" "%%~nxG" /Mov 1> NUL))))

Related

Replace URL Character Encodings In Windows Filenames (Batch)

I am trying to replace URL encodings (e.g. %20 as a placeholder for a space) with their corresponding ASCII values in all filenames in a Windows folder and its subfolders.
If have a simple .bat file that can accomplish this, but it has limitations:
#echo off
Setlocal enabledelayedexpansion
Set "Pattern0=%%20"
Set "Replace0= "
Set "Pattern1=%%27"
Set "Replace1='"
Set "Pattern2=%%28"
Set "Replace2=("
Set "Pattern3=%%29"
Set "Replace3=)"
Set "Pattern4=%%5B"
Set "Replace4={"
Set "Pattern5=%%5D"
Set "Replace5=}"
For %%# in ("D:\Dropbox\Music\*.mp3") Do (
Set "File=%%~nx#"
Ren "%%#" "!File:%Pattern0%=%Replace0%!"
Ren "%%#" "!File:%Pattern1%=%Replace1%!"
Ren "%%#" "!File:%Pattern2%=%Replace2%!"
Ren "%%#" "!File:%Pattern3%=%Replace3%!"
Ren "%%#" "!File:%Pattern4%=%Replace4%!"
Ren "%%#" "!File:%Pattern5%=%Replace5%!"
)
Pause&Exit
There are two major limitations I'd like to fix:
It only checks the ..\Music\ folder root. I'd like it to look at
files in subdirectories, too.
It exits the For loop as soon as one of the renames are executed
(all %20's replaced first pass, for example, but nothing else).
And surely there is a better way to specify the encodings and their replacements (rather than variable pairs for each), but that's a nice-to-have feature.
These encodings always take the form %XX, where X are hexadecimal values.
To solve directory recursive search, you could use dir /s /b instead
FOR /F "delims=" %%# in ('dir /s /b "D:\Dropbox\Music\*.mp3"') Do (
...
)
Do not rename the file any time, only after all replacements
FOR /F "delims=" %%# in ('dir /s /b "D:\Dropbox\Music\*.mp3"') Do (
set "file=%%~#"
set "file="!File:%%20= !"
set "file="!File:%%28=(!"
...
move "%%~#" "!file!"
)
I'm using move here, because it works with full pathnames, too
To solve problems with exclamation marks in filenames/paths, you need to toggle delayed expansion
setlocal DisableDelayedExpansion
FOR /F "delims=" %%# in ('dir /s /b "D:\Dropbox\Music\*.mp3"') Do (
set "file=%%~#"
setlocal EnableDelayedExpansion
set "file="!File:%%20= !"
set "file="!File:%%28=(!"
...
move "%%~#" "!file!"
endlocal
)

Move file to trimmed filename location with full name in Batch

I'm trying to move several similar files into folders based on the filename.
The code below works fine but does not work if the base name is more than 5 characters, then it says the directory already exists, and moves the files to the shorter named folder.
The idea is to make folders based on a text file, and move it together with pictures which start with the same name (up to an "_" to that same folder, while the filenames remain intact. The picture names are longer though with varying lengths.
eg:
SB12.txt
SB123.txt
SB1234.txt
SB12345.txt
SB123_V_05062020.jpg
SB123_VT_05062020.jpg
SB12345_V_05062020.jpg
SB12345_VT_05062020.jpg
I tried adding delims=_ to the loop parameters but does not work like this :
for /f "tokens=* delims=_ " %%f in ('dir /b /on "%dir%\*%ext%"') do (
I already "solved" the longer name problem by changing the wildcard to >.* like the line below, but then the pictures don't get moved:
move "%dir%\!thefile!>.*" "%dir%\%yyyymmdd%\!thefile!\"
full code:
#echo off
setlocal
REM store current directory. Using separate variable makes it easier to change behavior too.
set dir=%cd%
REM make date fitting for folder needs
for /f "tokens=2-4 delims=/ " %%i in ('date /t') do set yyyymmdd=%%k\%%j\%%i
REM call subroutine for each supported extension.
call :dotxt .txt
REM call :dojpg .jpg
REM Main program done.
echo Press a key to close.
pause
exit /b
:dotxt
set ext=%1
REM loop through all files with the given extension.
for /f "tokens=*" %%f in ('dir /b /on "%dir%\*%ext%"') do (
REM trim the extension and use the base name as directory name.
setlocal EnableDelayedExpansion
set thefile=%%~nf
echo !thefile!
md "%dir%\%yyyymmdd%\!thefile!"
REM move all files that start with the same base name.
move "%dir%\!thefile!*.*" "%dir%\%yyyymmdd%\!thefile!\"
)
%SystemRoot%\explorer.exe %dir%\%yyyymmdd%
REM exit subroutine
exit /b
I think I might need an additional loop or another "set" option but can't get it figured out on my own.
A somewhat easier way, assuming you've already defined Dir and yyyymmdd
#Echo off
Set "Dir=Path of Root\"
Set "yyyymmdd=Define Date Substring"
::: { Set environment state for Macro Definitions
Setlocal DisableDelayedExpansion
(Set LF=^
%= Above Empty lines Required =%)
Set ^"\n=^^^%LF%%LF%^%LF%%LF%^^"
::: { Macro for relocation of files to folders using Substring of Filename
::: - Usage:
::: %MOVEIF%{Search String*.Extension}{Destination Directory}
%= Capture Arguments =%
SET MOVEIF=For %%n in (1 2) Do If %%n==2 (%\n%
%= Split arguments using braces as delims =%
For /F "Tokens=1,2 Delims={}" %%G in ("!ITEM!") Do (%\n%
%= Use Arg 1 as search pattern for files in current and sub directories =%
For /F "Delims=" %%F in ('dir "%%~G" /b /s') do (%\n%
%= Split the name of file from _ for use as folder name =%
For /F "Tokens=1 Delims=_" %%N in ("%%~nF") Do (%\n%
%= Test / Create Subfolder in target Subdirectory using Arg 2 =%
IF Not exist "%%~H\%%~N" MD "%%~H\%%~N"%\n%
%= Execute move =%
Move "%%~F" "%%~H\%%~N"%\n%
)%\n%
)%\n%
)%\n%
) Else Set ITEM=
::: }
::: - enable macro, execute with args.
Setlocal EnableDelayedExpansion
CD "%Dir%" && For %%x in (jpg txt) Do For %%p in (SB) Do (%MOVEIF%{%%~p*.%%~x}{%Dir%%yyyymmdd%}) 2> Nul
Pause
Exit /B 0
Note: It isn't necessary to use a macro to achieve this, however it makes it very easy to use the code for other search strings or directories, as there is no need to edit the macro, you just call it with different parameters.
This is not a particularly complex task – here is a possible script (see all the rem comments):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (path to directory containing target files)
set "_MASK=SB*.txt" & rem /* (file name pattern to find the master files,
rem which sub-directory names are derived from) */
set "_SUFF=_*.jpg" & rem /* (suffix pattern to be appended to the base
rem name of a master file to find related slave
rem files, which are to be moved in addition) */
rem // Change into the root directory:
pushd "%_ROOT%" && (
rem // Loop through the found master files:
for /F "delims= eol=|" %%F in ('dir /B /A:-D-H-S "%_MASK%"') do (
rem // Create sub-directory with base name of current master file:
md "%%~nF" 2> nul
rem // Move the master file into the sub-directory:
move "%%F" "%%~nF\" > nul
rem // Move related slave files into the sub-directory:
if exist "%%~nF%_SUFF%" move "%%~nF%_SUFF%" "%%~nF\" > nul
)
rem // Return from the root directory:
popd
)
endlocal
exit /B
With some help I was able to get my script working, it just doesnt like spaces bu that's fine for me:
#echo off
setlocal
REM store current directory. Using separate variable makes it
REM easier to change behaviour too.
set dir=%cd%
REM make date fitting for folder needs
for /f "tokens=2-4 delims=/ " %%i in ('date /t') do set yyyymmdd=%%k\%%j\%%i
REM call subroutine for each supported extension.
call :do .txt
call :do .jpg
REM Main program done.
echo Press a key to close.
pause
exit /b
:do
set ext=%1
REM loop through all files with the given extension.
for /f %%f in ('dir /b /on "%dir%\*%ext%"') do (
echo %%f
for /f "tokens=1 delims=_." %%m in ("%%f") do (
echo %%m
REM Make the folder
if not exist "%dir%\%yyyymmdd%\%%m" mkdir "%dir%\%yyyymmdd%\%%m"
echo %dir%\%%m\%%f
move ".\%%f" "%dir%\%yyyymmdd%\%%m"
)
)
REM %SystemRoot%\explorer.exe %dir%\%yyyymmdd%
REM exit subroutine
exit /b
Thanks for the efforts

How to rename Windows files using extracted substring from current names

I have a directory of academic papers that were named using the convention below:
Author1-(Year)-Title.pdf
For example,
Jones-(2011)-XXX.pdf
Smith-(2002)-YYY.pdf
Johnson-(2015)-ZZZ.pdf
I would like to rename them as
(2011)-Jones-XXX.pdf
(2002)-Smith-YYY.pdf
(2015)-Johnson-ZZZ.pdf
That is, to extract the year from the file name and put it in front.
I tried the following code, which did not work
Setlocal enabledelayedexpansion
Set "Year=2013"
Set "Replace="""
For %%a in (*.pdf) Do (
Set "NewName=(%year%)-%%~a"
Ren "%%a" "%NewName%-File:%Year%=%Replace%!"
)
Pause&Exit
In case XXX also contains hyphens I'd suggest using tokens=1,2* to stop parsing the remainder of the file name.
I'd also remove the parentheses, when the year is first place there is no need to further emphasize it.
#Echo off
for /f "tokens=1-2* delims=-()" %%A in (
'Dir /b "*-(*)-*.pdf"'
) do Ren "%%A-(%%B)-%%C" "%%B-%%A-%%C"
Sample output
> dir /b
2002-Smith-YYY.pdf
2011-Jones-XXX.pdf
2015-Johnson-ZZZ.pdf
Not tested
for /f "tokens=1,2,3 delims=-" %%a in ('dir /b "*.pdf"') do (
echo ren "%%a-%%b-%%c" "%%b-%%a-%%c"
)
this will only echo the intended rename command.If it looks ok remove the echo word.
Derived form this SO article - it works:
#ECHO OFF &SETLOCAL ENABLEDELAYEDEXPANSION
FOR %%x IN (*.pdf) DO (
FOR /f "tokens=1-3 delims=-" %%a IN ("%%~x") DO (
SET "Author=%%a"
SET "Year=%%b"
SET "Title=%%c"
ren %%x !Year!-!Author!-!Title!
)
)
Here is a reliable way of doing what you are asking for even if the author part contains - on its own. The title portion may even contain ( and ), but the author part must not. So this is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_FILES=.\*-(????)-*.pdf" & rem // (basic pattern to match correct files)
set "_REGEX=^[^()][^()]*-([0123456789][0123456789][0123456789][0123456789])-.*\.pdf$" ^
& rem // (precise filter to exclude files violating the demanded pattern)
if not defined _REGEX set "_REGEX=.*" & rem // (avoid trouble with empty filter)
rem // Loop through all matching files:
for /F "eol=: tokens=1,2,* delims=()" %%F in ('
dir /B /A:-D "%_FILES%" ^| findstr /I /R /C:"%_REGEX%"
') do (
rem // Store the extracted file name parts:
set "LEFT=%%F" & set "YEAR=%%G" & set "REST=%%H"
setlocal EnableDelayedExpansion
rem // Reassemble the name and rename the file:
ECHO ren "!LEFT!(!YEAR!)!REST!" "(!YEAR!)-!LEFT!!REST:*-=!"
endlocal
)
endlocal
exit /B
After having verified the correct output, remove the upper-case ECHO command to actually rename files.

Move files into folders based on their names

I have some files in the form:
filename1 1.ext
filename1 2.ext
filename1 3.ext
...
filename2 1.ext
filename2 100.ext
...
filename20 1.ext
filename20 15.ext
(etc.)
...where filename can contain spaces.
And I want to move them to folder filename1, filename2, etc., respectively.
I know I can do a for loop for %%i in (*.ext) do and remove the extension with set folder=%%~ni. So what I am missing is how to remove everything after the space just before the number, and get only filename1, for example.
I also know I can split variable folder, but in this case I do not know by at which character I need to split, although I know it will be a space followed by a number.
So basically, I want something like this:
#echo off
set folder=
for %%i in (*.ext) do set folder=%%~ni & set folder=getfoldernamefromvariablefoldersomehow & mv %%i %folder%
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.ext" '
) DO (
CALL :sub1 "%%a" %%a
)
GOTO :EOF
:sub1
SET "filename=%~1"
:subloop
SHIFT
SET "numname=%~1"
IF NOT "%~2"=="" GOTO subloop
CALL SET "dirname=%%filename: %numname%=%%
ECHO( MD "%sourcedir%\%dirname%" 2>nul
ECHO( MOVE "%sourcedir%\%filename%" "%sourcedir%\%dirname%\%numname%"
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories.
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Perform a directory list of the required files in basic form without directorynames. Send the full fulename in quotes and without to the subroutine sub1.
In the subroutine, save the source filename in filename then shift each parameter supplied until there is no second parameter; the value in numname must then be the last or required filename.
Remove numname with a leading space from filename to get the required subdirectoryname, make that subdirectory and move the file.
[edit in the light of comment]
#ECHO OFF
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*.ext" '
) DO (
CALL :sub1 "%%a" %%a
)
GOTO :EOF
:sub1
SET "filename=%~1"
SET "destdirname=%~2"
:subloop
SHIFT
SET "numname=%~1"
IF NOT "%~2"=="" GOTO subloop
CALL SET "dirname=%%filename: %numname%=%%
ECHO( MD "%sourcedir%\%destdirname%" 2>nul
ECHO( MOVE "%sourcedir%\%filename%" "%sourcedir%\%destdirname%\%numname%"
GOTO :eof
It's difficult to scry your intentions when you give no example.
destdirname is set to the second parameter on entering sub1 which will be the first group of characters before the first space.
the md does not need to be gated since the 2>nul will suppress the directory exists error message.
Here is a script that does what you want. It splits off the last SPACE followed by numerals from the file name and uses the remaining string as the name of the destination directory of the movement.
This approach handles all valid characters for file names properly, even ^, &, %, !, ( and ). It can even handle file names that contain SPACE plus numerals plus .ext again correctly.
So here is the code:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=."
set "_TARGET=."
for /F "eol=| delims=" %%F in ('
dir /B "%_SOURCE%\*.ext" ^| findstr /R /I /C:" [0123456789][0123456789]*\.ext$"
') do (
set "FILE=%%F"
call :SPLIT LAST REST "%%F"
setlocal EnableDelayedExpansion
2> nul mkdir "!_TARGET!\!REST!"
ECHO move /Y "!_SOURCE!\!FILE!" "!_TARGET!\!REST!"
endlocal
)
endlocal
exit /B
:SPLIT rtn_last rtn_rest val_string
setlocal DisableDelayedExpansion
set "RES=" & set "STR=%~3"
:LOOP
for /F "tokens=1,* delims= " %%I in ("%STR%") do (
if "%%J"=="" (
set "RES=%%I"
) else (
set "STR=%%J"
goto :LOOP
)
)
set "STR=%~3|"
call set "STR=%%STR: %RES%|=%%"
(
endlocal
set "%~1=%RES%"
set "%~2=%STR:^^=^%"
)
exit /B
After having tested the script, remove the upper-case ECHO command to actually move any files. Unless you remove the /Y option from the move command, files become overwritten without prompt. To suppress summary messages (like 1 file(s) moved.), add > nul to the move command line. Note that any prompt was also hidden then in case you removed the /Y option.
Thank to all of you for your comments. Finally, i was able to get a solution:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "_source=C:\Users\kurok_000\Downloads"
set "_target=C:\Users\kurok_000\YandexDisk\Mangas"
for /f "eol=| delims=" %%f in ('dir /b "%_source%\*.7z"') do (
call :fixNames "%%f" %_source%
)
for /f "eol=| delims=" %%f in ('dir /b "%_source%\*.7z" ^| findstr /r /i /c:" [0123456789][0123456789]*\.7z$"') do (
set "file=%%f"
call :split last rest "%%f"
setlocal EnableDelayedExpansion
2> nul mkdir "!_target!\!rest!"
move /y "!_source!\!file!" "!_target!\!rest!" >nul
echo moved %%f to !_target!\!rest!
endlocal
)
endlocal
exit /b
:split rtn_last rtn_rest val_string
setlocal DisableDelayedExpansion
set "res=" & set "str=%~3"
:loop
for /f "tokens=1,* delims= " %%i in ("%str%") do (
if "%%j"=="" (
set "res=%%i"
) else (
set "str=%%j"
goto :loop
)
)
:quit
set "str=%~3|"
call set "str=%%str: %res%|=%%"
(
endlocal
set "%~1=%res%"
set "%~2=%str:^^=^%"
)
exit /b
:fixNames _file _folder
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FILE=%1"
set "FILE=%file:~1,-1%"
set "folder=%2"
for /F "tokens=1,* delims=0123456789" %%A in ("%FILE%") do (
set filename=!FILE:%%B=!%%~xB
)
if not "%filename%"=="%FILE%" (rename "!folder!\!FILE!" "!filename!")

Batch Command to Move Files from Sub Directories to New Directory

I am attempting to use the batch file below to move files from one folder to another. The batch commands will create sub folders within the destination folder based on the create dates against each file in the source folder.
The problem is that the source folder contains sub folders and the batch commands cannot recurse into sub folders.
Please advise how to modify the batch file to allow recurse into sub folders on the source folder.
Thanks
Rialet
echo %1 "-" %2
If [%1]==[] ECHO "Source Directory parameter required"&GOTO :EOF
If [%2]==[] ECHO "Target Directory parameter required"&GOTO :EOF
SET TrimQuote=%2
for /f "useback tokens=*" %%T in ('%2') do set TrimQuote=%%~T
REM echo %TrimQuote%
::loop through files only
For /F "TOKENS=1 DELIMS=%_TabSpace%" %%B In ('dir %1 /a-d /B /OD') DO (
REM echo "%%B - " %%B
For /F "TOKENS=1 DELIMS=%_TabSpace%" %%D In ('dir %1\"%%B" /a-d /OD ^| findstr /B [0-9][0-9]/[0-9]') DO (
REM echo "%%D - " %%D
for /F "tokens=1,2,3,4 delims=/ " %%b in ("%%D") do (
REM echo "b = " %2\%%c%%a\%%b
REM echo %2\%%d%%c\%%b
if NOT exist %2\%%d%%c\%%b md %2\%%d%%c\%%b
move %1\"%%B" %2\%%d%%c\%%b\
)
)
)
Try using
For /F "TOKENS=1 DELIMS=%_TabSpace%" %%B In ('dir %1 /a-d /S /B /OD') DO (
the /s will cause recursion. The downside is that the output of the dir command is then d:\path\file.ext - which may not marry well with your "TOKENS=1 DELIMS=%_TabSpace%". You'd probably need to use "delims=" (ie, no delimiters, hence entire line in token1).
You can then retrieve the various parts of the full-filename as %%~dB, %%~pB, %%~nB and %%~xB (the drive, path, naem and extension - and you can combine these parts if you wish by using %%~nxB for name+extension, for instance.
Supplemental info - batch commented.
#ECHO OFF
SETLOCAL
:: The above two lines are a traditional batch introduction.
:: The first turns `ECHO`ing of the command to the console OFF
:: The second makes all changes to the environment 'local'
:: which means that any variable changes made during the batch
:: will be undone at the end, restoring the original environment.
:: Note that the official remarks/comments method is
REM This is a remark
:: But the double-colon method is commonly used as :: is less intrusive
:: Echo the two parameters given to the batch (%1 and %2)
echo %1 "-" %2
:: The original parameter-present detection is weak. This is a better method
SET target=%~1
If not defined target ECHO "Source Directory parameter required"&GOTO :EOF
SET target=%~2
If not defined target ECHO "Target Directory parameter required"&GOTO :EOF
:: Note `trimquote` (batch is largely case-insensitive) is a meaningless name
:: New name TARGET is better. Setting to %~2 removes enclosing quotes from
:: string assigned to variable.
::loop through files only
:: `"delims="` means there are no delimiters, so the entire line is assigned to
:: the variable `%%B` (FOR loop variablenames ("metavariables") ARE case-sensitive!)
:: The line being assigned comes from the output of the `DIR` command
:: which is filenames only (/a-d) in subdirectories (/s) in basic form (/b)
:: (ie name only, no dates, sizes, headers or summary) and in order of date (/od)
For /F "DELIMS=" %%B In ('dir "%~1" /a-d /S /B /OD') DO (
REM echo "%%B - " %%B
REM within a FOR loop, better to use REM remarks than :: remarks (version-dependent)
REM I believe the intention of the original here was to pick up the filedate
REM It wouldn't work since FINDSTR is looking for lines that begin (/B) with
REM 2 digits, a slash and one digit, but the date format about to be processed...
REM For /F "TOKENS=1 DELIMS=%_TabSpace%" %%D In ('dir %1\"%%B" /a-d /OD ^| findstr /B [0-9][0-9]/[0-9]') DO (
REM echo "%%D - " %%D
REM Process date - 4 elements separated by space or /. Pick the last three
REM so implictly format is DAYNAME xx/yy/zz BUT the elements would be applied
REM to %%b, %%c, %%d, %%e
REM for /F "tokens=1,2,3,4 delims=/ " %%b in ("%%D") do (
for /F "tokens=1,2,3,4 delims=/ " %%a in ("%%~tB") do (
REM echo "b = " %2\%%c%%a\%%b
REM echo %2\%%d%%c\%%b
REM Make a new directory. 2>nul suppresses error message if already exists
md "%TARGET%\%%d%%c\%%b" 2>nul
move "%%B" "%TARGET%\%%d%%c\%%b\"
)
)
Bit of a nightmare really - No idea of what format date you are using, nor what format target directory structure you want. This should "flatten" the structure, so any file filename.ext would be placed in %target%\xx\yy\zz regardless of where in your source structure the file originally resides. There is also no protection about multiple instances of the same filename.ext with the same DATE but in different subdirectories in the source. Need a lot more clarification of the entire scenario to be more certain. Really just commenting and changing the existing (presumed-working but evidently-faulty) batch...
You can also use the XCOPY function to copy a parent and child folders.

Resources