dir subtree with regex path string - cmd

I have file tree like this:
C:\Users\user\AppData\Local\app-package\blabla
C:\Users\user\AppData\Local\app-package\blabla\something
C:\Users\user\AppData\Local\app-package\app-1.1.6
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources\app.package.unpacked
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources\app.package.unpacked\daemon
I've tried command
D:\tmp>dir /B/S/AD "%LOCALAPPDATA%\app-package\app*"
Expect:
C:\Users\user\AppData\Local\app-package\app-1.1.6
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources\app.package.unpacked
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources\app.package.unpacked\daemon
Real output:
C:\Users\user\AppData\Local\app-package\app-1.1.6
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources\app.package.unpacked
Why it happens and how can I fix it?

You seem to think that the wildcard app* only matches against items in the given target directory, but actually it matches against the last element of a path. That explains why:
dir /B/S/AD "%LOCALAPPDATA%\app-package\app*"
provides this result:
C:\Users\user\AppData\Local\app-package\app-1.1.6
C:\Users\user\AppData\Local\app-package\app-1.1.6\resources\app.package.unpacked
Change app* to app-* and the output will only be this:
C:\Users\user\AppData\Local\app-package\app-1.1.6
I think what you need is the following:
pushd "%LOCALAPPDATA%\app-package" && (for /F "eol=| delims=" %I in ('dir /B /A:D-H-S "app-*"') do #echo(%~fI& dir /S /B /A:D-H-S "%I" & popd)
To better understand what this does, here is a batch-file version with some explanatory comments:
#echo off
rem // Change into the target directory:
pushd "%LOCALAPPDATA%\app-package" && (
rem // Loop through matching directories within target directory:
for /F "eol=| delims=" %%I in ('dir /B /A:D-H-S "app-*"') do (
rem // Return matching sub-directory itself:
echo(%%~fI
rem // Return contents of matching sub-directory:
dir /S /B /A:D-H-S "%%I"
)
rem // Return from the target directory:
popd
)

Related

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

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 parameters with spaces for loop recursive move

I am trying to put a batch together to move all zip files back one directory from the current directory in a recursive move where I specify the search directory from the parent.
In example, the current directory is download and the sub to recursive search is a command line parameter.
I then want to move the *.zip back one directory from download. I don't want to search in any other directory other than the one I specify.
>movezipdir junk 2009
This outputs junk 2009. Then junk was unexpected at this time.
Here's what I have but the for loop doesn't like the variable with spaces...
#echo off
IF %1.==. GOTO No1
set DirName=%*
echo %DirName%
For /d %DirName% in ( *.* ) do (
For /d %%e in (""%DirName%"\*") do (
rem move /Y "%%e\*.zip" "%CD%\.."
echo %DirName%
rem rd /S /Q "%%e"
)
rem rd /S /Q %DirName%
)
GOTO End1
:No1
ECHO No Directory Specified
GOTO End1
:End1
It would be great if someone could point me in the right direction. Thanks. Ken
#echo off
rem :: check if the args is not nul
if [%1] == [] (
goto error
) else (
for /f "usebackq tokens=1*" %%i in ('echo %*') do (
set "dirname=%%~i"
)
)
rem check if the specified args correspond to a directory
if exist "%dirname%\nul" echo %dirname% it's a directory || goto error
set /a c=1
setlocal EnableDelayedExpansion
for /f "tokens=* delims=" %%a in ('dir /b /s /a-d "%dirname%\*.txt"') do (
rem usage: move [{/y | /-y}] [Source] [Target]
rem https://technet.microsoft.com/en-us/library/cc772528.aspx
rem http://ss64.com/nt/move.html
rem %cd% here are redundant because when target are omitted,
rem the target are always the current directory
echo move /-y "%%a" "%cd%"
rem break loop after 5 iterations.
rem You can remove it if the test are Okay
if [!c!] equ [5] goto break_loop
set /a c+=1
)
:break_loop
rem usage: RD [/S] [/Q] [drive:]path
echo rd /s /q %dirname%
exit /b 0
:error
echo Error: No directory specified or invalid arguments
echo Usage: %~0 ^<full path of the directorys^>
echo Example: %~0 c:\user\janne\MyDocuments
exit /b 0

How to store and use the value of a batch file for loop variable

I have a Windows batch file where I'm trying to perform some operation on every file with a given name within a directory tree.
for /f "delims=" %%i in ('dir /s %1 ^| findstr /c:"Directory of"') do (
set dir=%%i
echo i = %%i
echo dir = %dir:~14%
)
My problem is that dir is always equal to the last element found in the for loop. So I may get output like this. (alignment added for clarity)
i = Directory of c:\alpha\myfile.txt
dir = c:\charlie\myfile.txt
i = Directory of c:\bravo\myfile.txt
dir = c:\charlie\myfile.txt
i = Directory of c:\charlie\myfile.txt
dir = c:\charlie\myfile.txt
So dir is never equal to "alpha" or "charlie".
What do I need to do to get the functionality I'm looking for?
Try:
for /f "delims=" %%i in ('dir /s %1 ^| findstr /c:"Directory of"') do (
set dir=%%i
echo i = %%i
call :process
)
goto :eof
:process
echo dir = %dir:~14%
goto :eof
Note that within the internal subroutine :process, %%i is not available. The call :process requires the colon and goto :eof means go to physical end-of-file (which terminates the subroutine) where the colon is required but :EOF is understood and shouldn't be assigned as a label.
Possibly better would be
for /f "delims=" %%i in ('dir /s /b %1') do (
set dir=%%i
echo i = %%i
call :process
)
goto :eof
:process
echo dir = %dir%
goto :eof
YMMV
two examples:
for /f "tokens=2*" %%a in ('dir /s ^|findstr /bc:" Directory of"') do echo %%~b
for /r %%a in (.) do echo %%~fa
This reports the path and also the full filespec.
#echo off
for /r %%a in (myfile.txt) do echo "%%~dpa" and full path is "%%a" & pause

Resources