I have put together a small batch file program to create a playlist
#echo off
DIR /S /o:n /b *.avi > Playlist.m3u
Is there a way to change this so that it will sort in a random order each time it is run?
It can be done, but it won't be pretty! Can you use a better platform instead of batch files? Maybe this is just the opportunity you've been waiting for to learn Powershell! :-)
However, if you insist on batch, here's the general approach I'd take if I were to try:
Count the number of .avi files in your folder.
Pick a random number between 0 and this number. For example, set /a randomLineNum=%random% %% 10 will set %randomLineNum% to a number from 0 to 9.
Use something like for /f "skip=%randomLineNum%" %%L in ('dir /s /o:n /b *.avi') ... to grab that random line, and echo %%L > Playlist.m3u.
Go back to #2.
That simplistic approach will end up with duplicates, and I didn't build in any way to exit the loop. I leave those problems for you to solve (or to ask in a future question). :-)
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
::
:: establish a tempfile name
::
:temploop
SET tempfile="%temp%\temp%random%.tmp"
IF EXIST %tempfile% GOTO temploop
::
:: Write the list of filenames with each
:: prefixed by a random number and a colon
::
(FOR /f "delims=" %%i IN (
'dir /s/b *.avi'
) DO ECHO !random!:%%i
)>%tempfile%
::
:: Write the playlist.
:: sort the tempfile (which places it
:: in random order) and remove the number:
::
(FOR /f "tokens=1*delims=:" %%i IN (
' sort ^<%tempfile% ') DO ECHO %%j
) >playlist.m3u
::
:: and delete the tempfile.
::
DEL %tempfile% 2>NUL
Should work - but it will have difficulties if your file/pathnames contain !
Documentation in the code.
Solution without a TEMP file:
#echo off &setlocal
set "playlist=Playlist.m3u"
del %playlist% 2>nul
set /a files=0
for %%i in (*.avi) do set /a files+=1
if %files% equ 0 (echo No AVI found&goto:eof) else echo %files% AVI's found.
set /a cnt=%files%-1
for /l %%i in (0,1,%cnt%) do for /f "delims=" %%a in ('dir /b /a-d *.avi^|more +%%i') do if not defined $avi%%i set "$avi%%i=%%a"
:randomloop
set /a rd=%random%%%%files%
call set "avi=%%$avi%rd%%%"
if not defined avi goto :randomloop
set "$avi%rd%="
>>%playlist% echo %avi%
set /a cnt-=1
if %cnt% geq 0 goto:randomloop
echo Done!
endlocal
This doesn't use DelayedExpansion, so it can handle files with exclamation marks in its name. It takes a bit more time, but also doesn't need a temp file.
Related
Am trying to use substring manipulation within a FOR loop and I can't get it to work for love nor money. I've been told that we can't use substring manipulation on a loop variable (%%f etc), so you have to set another variable to equal the loop (set MyVariable=%%f) and then use a substring option to work on this decendent (set MyOtherVar=%MyVariable:~0,-3%). However when echoing these variables only the loop variable is set/non-null.
Here's the code I'm currently using.
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET OutPref="373137#"
SET OutSuf1="#Window Clean POD "
SET OutSuf2="#1520755.pdf"
SET MatchStr=#mydomain.com
cd c:\Oscar\Scripts\FileNamer\Inbound\
for /f "tokens=*" %%f in ('dir /b *.pdf') do (
c:\Oscar\Scripts\FileNamer\pdftotext.exe -q %%f
set InPdfVer=%%f
set InTxtVer=%InPdfVer:~0,-3%txt
echo Loop Val= %%f
echo InPdfVer= %InPdfVer%
echo InTxtVer= %InTxtVer%
pause
set InAddLine=findstr %MatchStr% %InTxtVer%
set stemp=%InAddLine%
set pos=0
:loop
set /a pos+=1
echo %stemp%|findstr /b /c:"%MatchStr%" >NUL
if errorlevel 1 (
set stemp=%stemp:~1%
if defined stemp GOTO loop
set pos=0
)
set /a pos-=3
call set StoreNo=%InAddLine:~%pos%,-25%
call:getvalue C:\Oscar\Scripts\FileNamer\StoreList.inf %StoreNum% StoreName
set OutFile=%OutPerf%%StoreNo%%OutSuf1%%StoreName%%OutSuf2%
move %%f c:\Oscar\Scripts\FileNamer\Outbound\%OutFile%
)
cd c:\Oscar\Scripts\FileNamer\
exit 0
:getvalue
rem This function reads a value from a file and stored it in a variable
rem %1 = name of file to search in
rem %2 = search term to look for
rem %3 = variable to place search result
FOR /F "tokens=1,2* delims==" %%i in ('findstr /b /l /i %~2= %1') DO set %~3=%%~j
goto:eof
Hope this makes sense, can try and explain it. Quite likely the bottom part doesnt work either but didnt get that far!
Thanks for any thoughts either way, as a general overview the script is supposed to take PDF files in the incoming folder, convert them to text, search for an email address in that file, look that email address in an external list and then move the PDF file (renaming the file with an aggreed convention in the process) and then move onto the next file, in a loop, until the end of the matching files.
Kind regards,
Oscar
OK so the rest of it seems to what what it should now but I still can't get this substring to set, I just end up with the whole string in the decendent variable. Here's the new code (please excuse the pauses and echos used for troubleshooting).
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET OutPref=373137#
SET OutSuf1=#Window Clean POD
SET OutSuf2=#1520755.pdf
SET MatchStr=#mydomain.com
cd c:\Oscar\Scripts\FileNamer\Inbound\
for /f "tokens=*" %%f in ('dir /b *.pdf') do (
c:\Oscar\Scripts\FileNamer\pdftotext.exe -q %%f
set InPdfVer=%%f
call set InTxtVer=!InPdfVer:~0,-3!txt
for /f "tokens=*" %%x in ('findstr !MatchStr! !InTxtVer!') do set InAddLine=%%x
call:getpos
echo !pos!
pause
call set StoreNo=!InAddLine:~!pos!,-25!
call:getvalue C:\Oscar\Scripts\FileNamer\StoreList.inf !StoreNum! StoreName
echo OutPerf !OutPref!
echo StoreNo !StoreNo!
echo OutSuf1 !OutSuf1!
echo StoreName !StoreName!
echo Outsuf2 !OutSuf2!
set OutFile=!OutPerf!!StoreNo!!OutSuf1!!StoreName!!OutSuf2!
echo %%f !OutFile!
pause
REM move %%f c:\Oscar\Scripts\FileNamer\Outbound\!OutFile!
)
cd c:\Oscar\Scripts\FileNamer\
exit /b
:getpos
set stemp=!InAddLine!
set pos=0
:loop
set /a pos+=1
echo !stemp!|findstr /b /c:"!MatchStr!" >NUL
if errorlevel 1 (
set stemp=!stemp:~1!
if defined stemp GOTO loop
set pos=0
)
set /a pos-=3
goto:eof
:getvalue
rem This function reads a value from a file and stored it in a variable
rem %1 = name of file to search in
rem %2 = search term to look for
rem %3 = variable to place search result
FOR /F "tokens=1,2* delims==" %%i in ('findstr /b /l /i %~2= %1') DO set %~3=%%~j
goto:eof
Thanks all for your inputs, here's the finished script with a few more updates.
Makes use of pdftotext.exe as part of the freeware xpdf suite (please donate as it's a great utility) and in this case some lookup files that help resolve a site number to its description. In the format of
001=My Town
I totally failed to get a workable way to pad 2 digit site codes with a leading 0 to make all sites 3 digits but ended up doing the same thing with another lookup file!
I hope this is of some use to someone else!
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET OutPref=373137#
SET OutSuf1=#My Text
SET OutSuf2=#1520755.pdf
SET MatchStr=#mydomain.com
SET BaseDir=C:\myfolder\
SET LogFile=C:\myfolder\FileNamer.log
SET InFolder=C:\myfolder\Inbound\
SET OutFolder=C:\myfolder\Outbound\
SET StoreList=C:\myfolder\StoreList.inf
SET StoreNoConv=C:\myfolder\StoreNoConv.inf
echo Starting Run %TIME% %DATE% >> %LogFile%
echo Starting Run %TIME% %DATE%
cd %InFolder%
for /f "tokens=*" %%f in ('dir /b *.pdf') do (
%BaseDir%pdftotext.exe -q %%f
set "InTxtVer=%%~nf.txt"
for /f "tokens=*" %%x in ('findstr !MatchStr! !InTxtVer!') do set InAddLine=%%x
call:getpos
call set StoreNo=%%InAddLine:~!pos!,-25%%
echo Now Renaming Store No !StoreNo!
call:getvalue %StoreList% !StoreNo! StoreName
call:getvalue %StoreNoConv% !StoreNo! ThreeDigitNo
set OutFile=!OutPref!Store!ThreeDigitNo!!OutSuf1!!StoreName!!OutSuf2!
echo %%f moved to !OutFile! >> %LogFile%
move "%%f" "%OutFolder%!OutFile!" >> %LogFile%
del !InTxtVer!
)
cd %BaseDir%
exit /b
:getpos
set stemp=!InAddLine!
set pos=0
:loop
set /a pos+=1
echo !stemp!|findstr /b /c:"!MatchStr!" >NUL
if errorlevel 1 (
set stemp=!stemp:~1!
if defined stemp GOTO loop
set pos=0
)
set /a pos-=4
goto:eof
:getvalue
rem This function reads a value from a file and stores it in a variable
rem %1 = name of file to search in
rem %2 = search term to look for
rem %3 = variable to set search result under
FOR /F "tokens=1,2* delims==" %%i in ('findstr /b /l /i %~2= %1') DO set %~3=%%~j
goto:eof
I am writing a batch program for controling my movie archive (Personel usage). This is what i am trying to do for copying folders.
:_Kopya
set "TRGT=%~1" & set "KPY-GLN[1]=%~2" & set "KPY-GLN[2]=%~3" & set "KPY-GLN[3]=%~4"
REM Checking user input and defining variables.
for /l %%s in (1,1,3) do (
if DEFINED KPY-GLN[%%s] (
for /f "tokens=1-2 delims=:" %%a in ("!KPY-GLN[%%s]!") do (
call :_Kontrol "%%a" "%%b" "" "" "aaaaa[%%s]" "bbbbb[%%s]" "" ""
if "!TEST!"=="0" goto :EOF
)
)
)
REM Copying folders.
for /l %%s in (1,1,3) do (
if NOT DEFINED bbbbb[%%s] set bbbbb[%%s]=!aaaaa[%%s]!
for /l %%a in (!aaaaa[%%s]!,1,!bbbbb[%%s]!) do (
call :_ReadLine "%MURL%" "%%a" "LINE"
if EXIST "!TRGT!\!LINE:~20!" rd /s /q !TRGT!\!LINE:~20!
robocopy /s /e "!LINE!" "!TRGT!\!LINE:~20!" >NUL 2>&1
)
)
goto :EOF
And this is the way i call,
call :_Kopya "C:\" "123:125" "124:130" "125"
Which means copy the file numbers from 123 to 125 and from 124 to 130 and 125.
It works fine but there is a problem i want to solve. When i call this function the way i show its copying file number 124 2 times and file number 125 3 times. How can i fix this issue?
PS1: %MURL% its a text file and contains local address of those files. Its something like this M:\Movies\000y.001y\The.Lord.of.the.Rings.The.Return.of.the.King.(2003){0167260}[00087]
PS2: :_ReadLine its a function that reading specific line and adding value of this line to LINE variable.
#ECHO Off
SETLOCAL
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
:: Parameters are adirectory range*
:: where range may be a single number or start:finish
SET "directory=%~1"
:loop
SHIFT
IF "%~1"=="" GOTO :eof
FOR /f "tokens=1,2delims=:" %%a IN ("%~1") DO (
IF "%%b"=="" (CALL :kopythis %%a) ELSE (FOR /L %%c IN (%%a,1,%%b) DO CALL :kopythis %%c)
)
GOTO loop
GOTO :EOF
:kopythis
IF DEFINED $%1 GOTO :EOF
SET $%1=Y
ECHO(COPY whatever with parameters %directory% and %1
GOTO :eof
This should do what you seem to need. I'll leave you to work out the details of how to structure whatever copy mechanism you need from the parameters provided.
Note that with this approach, quoting the parameters is optional with the obvious exception of the first when it's optional if the first doesn't contain separators. It also allows any number of range parameters.
I'm making a script that copies random folders from source to destination until that folder is full (knowing that the destination folder is smaller then the source.
All the individual parts are working as intended, but I'm now running the script without the copy included (just an echo) so basically if there aren't any folders larger then the remaining space in the destination directory, it should be running for ever.
But it stops at an average of 5 iterations while there isn't any folder large enough to fill up the directory. This is the code I'm using
EDIT: got it olmost working, but now sometimes a needed_space outputs 0
#ECHO off
setlocal EnableDelayedExpansion
SET n=0
SET SOURCE_PATH=M:\Movies
SET DEST_PATH=E:\Movies
:: get all folders in dir
FOR /f "usebackq tokens=*" %%a in (`DIR /b/a:d %SOURCE_PATH%`) do (
SET /A n+=1
SET folder[!n!]=%%a
)
:loop
:: selecting ranodm number
SET /A rand=(n*%random%)/32768+1
:: check for space
for /F "tokens=3" %%S in ('dir /-c "%SOURCE_PATH%\!folder[%rand%]!\*" ^| findstr /c:"File(s)"') DO set NEEDED_SPACE=%%S
FOR /f "tokens=1*delims=:" %%i IN ('fsutil volume diskfree %DEST_PATH%') DO SET FREE_SPACE=%%j
ECHO %FREE_SPACE% - %NEEDED_SPACE%
IF %NEEDED_SPACE% GTR %FREE_SPACE% GOTO done
:: check if file does not exits
IF EXIST %DEST_PATH%\!folder[%rand%]! GOTO loop
:: copy file
ECHO moving %SOURCE_PATH%\!folder[%rand%]! to %DEST_PATH%\!folder[%rand%]!
:: again
GOTO loop
:done
ECHO Done copying random folders, have fun!
1.- You dont have taken in consideration the environment space exhausting. Maybe you can not create the array you are intending.
2.- fsutil volume diskfree ... always return the space in VOLUME, no matter if you indicate a directory
3.-if command do numeric comparisons ONLY when all the characters in both sides of the operator are numeric. Your () disables it, so (10) is less than (3)
Ok, this is working so far. waiting for the disk to get full if that works, this should be sufficient (not 100% perfect check but close enough)
code:
#ECHO off
setlocal EnableDelayedExpansion
SET n=0
SET SOURCE_PATH=M:\src
SET DEST_PATH=E:\dest
:: get all folders in dir
FOR /f "usebackq tokens=*" %%a in (`DIR /b/a:d %SOURCE_PATH%`) do (
SET /A n+=1
SET folder[!n!]=%%a
)
:loop
:: selecting ranodm number
SET /A rand=(n*%random%)/32768+1
:: check for space
for /F "tokens=3" %%a in ('dir /-c "%SOURCE_PATH%\!folder[%rand%]!" ^| findstr /c:"File(s)"') do set bytesfree=%%a
set bytesfree=%bytesfree:,=%
set /a NEEDED_SPACE=%bytesfree:~0,-3%
for /f "tokens=3" %%a in ('dir %DEST_PATH%\') do set bytesfree=%%a
set bytesfree=%bytesfree:,=%
set /a FREE_SPACE=%bytesfree:~0,-3%
ECHO %FREE_SPACE% - %NEEDED_SPACE%
IF %NEEDED_SPACE% GTR %FREE_SPACE% GOTO done
:: check if file does not exits
IF EXIST %DEST_PATH%\!folder[%rand%]! GOTO loop
:: copy file
set src="%SOURCE_PATH%\!folder[%rand%]!\*"
set dest= "%DEST_PATH%\!folder[%rand%]!\*"
ECHO moving %src% to %dest%
xcopy /s /e /i %src% %dest%
:: again
GOTO loop
:done
ECHO Done copying random folders, have fun!
I have a directory with many sub-directories that contain thousands of jpgs. What I want to do is create a batch script that will go through all of the sub-directories and delete every 2nd image i.e. keep the first, third, fifth image but delete the second, fourth, and six image etc per directory (ordered by filename).
I tried with the following but my knowledge of batch scripting is poor, and the syntax is clearly incorrect.
#echo off
set z = 0
for /f %%a in ('dir/b *.jpg')
do (
set z = z + 1
if z == 2 del %%a
)
The DO must be on the same line as FOR.
You must use SET /A if you want to do math
Your logic is wrong - Currently it will only delete the 2nd file, not every other one. You should take the mod 2 value (remainder devided by 2) and delete if the result is 0.
You must use %z% if you want to see the current value (except within a SET /A statement). But that will not work inside a code block that just set the value. In that case you need to enable delayed expansion and use !z! instead.
Expanding a FOR variable that contains ! (valid in file names) while delayed expansion is enabled will corrupt the value. So delayed expansion must be toggled on and off
You say you want to recurse sub-directories, but your code only looks at one folder.
Spaces are significant in the SET statement. Your code defines a variable z with a space at the end of the name. Not what you want.
Here is a debugged version:
#echo off
setlocal
for /r %%D in (.) do (
set "z=0"
for /f %%F in ('dir /b "%%D\*.jpg"') do (
set /a "z+=1, r=z%%2"
setlocal enableDelayedExpansion
if !r! equ 0 del "%%D\%%F"
endlocal
)
)
There are ways to solve this without delayed expansion. One is to simply alternate between defining and undefining a variable.
#echo off
setlocal
for /r %%D in (.) do (
set "del="
for /f %%F in ('dir /b "%%D\*.jpg"') do if defined del (
del "%%D\%%F"
set "del="
) else set "del=1"
)
Another is to intentionally divide by 0 when you want to delete, and delete only when there is an error. Error messages are hidden by 2>nul, and the || operator conditionally executes the following command only if the prior command failed.
#echo off
setlocal
for /r %%D in (.) do (
set "z=0"
for /f %%F in ('dir /b "%%D\*.jpg"') do 2>nul set /a "z+=1, 1/(z%%2)" || del "%%D\%%F"
)
try this and remove the echo if the output looks good:
#echo off &setlocal
for /f "tokens=1*delims=:" %%a in ('dir /b /s /a-d *.jpg^|findstr /n $') do (
echo %%a|findstr "[02468]$" >nul && echo del "%%~b"
)
Is it possible to write a batch file that deletes all files in a directory for which the first n characters of the file's root name do not match the first n characters of any other filenames in that directory? For instance, suppose the directory contains the following:
Purcell_HenryA.txt
Purcell_HenryB.txt
Casaubon_IsaacA.txt
In this case, we would want to delete all files in the directory whose first 13 characters did not match the first 13 characters in any other files in the directory. (That is, we'd want to delete only Casaubon_IsaacA.txt.) I have tracked down scripts that delete all files with unique extensions in a directory, but don't know how to begin to write this script, and would therefore be grateful for any leads on the question.
This checks for root filenames of 14 characters and over - and if there is only 1 file with the same leading 13 characters then it will echo del. Remove the echo to make it perform the deletion.
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('dir /b /a-d') do (
set "part=%%~na"
if not "!part:~13,1!"=="" (
set "part=!part:~0,13!"
for /f "delims=" %%b in ('dir /b /a-d "!part!*.*" ^|find /c "!part!" ') do (
if %%b EQU 1 echo del "%%a"
)
)
)
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET target=u:\testdir
DIR /b /a-d %target%
echo====^^ names IN DIR ^^===
SET length=13
SET match=:
SET "candidate="
FOR /f "delims=" %%i IN ('dir /b/a-d "%target%\*"') DO (
SET filename=%%i
SET section=!filename:~0,%length%!
IF !section!==!match! (SET "candidate=") ELSE (
IF DEFINED candidate ECHO(DEL %target%\!candidate!
SET candidate=%%i
SET match=!section!
)
)
IF DEFINED candidate ECHO(DEL %target%\!candidate!
GOTO :EOF
Test result:
abc123_uniquename.txt
another_uniquename.txt
duplicate_name1234.txt
duplicate_name1235.txt
duplicate_name1236.txt
hello.txt
repeated__name1236.txt
repeated__name1235.txt
unique__name1235.txt
===^ names IN DIR ^===
DEL u:\testdir\abc123_uniquename.txt
DEL u:\testdir\another_uniquename.txt
DEL u:\testdir\hello.txt
DEL u:\testdir\unique__name1235.txt
If you are happy after testing, remove both ECHO( to activate the delete function.
For this code file name = name+extension:
#echo off &SETLOCAL enabledelayedexpansion
FOR %%a IN (*) DO (
SET "search=%%~a"
IF "!search:~13!" neq "" (
FOR /f "delims=[]" %%b IN ('dir /b /a-d /on "!search:~0,13!*" ^| find /n "!search:~0,13!"') DO SET found=%%b
IF !found! equ 1 ECHO DEL "%%~a"
)
)
And because I coose a very similar solution as foxidrive here is another one:
#echo off &SETLOCAL enabledelayedexpansion
FOR %%a IN (*) DO (
SET search=%%a
IF "!search:~13!" neq "" SET /a $!search:~0,13!+=1 2>nul
)
FOR /f "tokens=1*delims=$=" %%a IN ('set "$"') DO if %%b equ 1 echo del "%%~a*"
The way i'd go about this is as follows, i will explain the logic and i'll leave you to do the coding.
You will parse all the file names into variables, while increasing each time.
Then you will set a limit to the number of loops to go through. Then you will search the first 13 characters of the file name and if the number of lines is equal to 1 then delete it. After you will increase the variable by 1 and go through the loop, at the end of each loop it will check if it has reached the limit aka the number of files in the directory, if it has reached the limit, end the loop, otherwise continue.
hah, i finally decided to do it after a guy decided to use my idea i described into actual code, anyway this is way shorter and a lot faster than his, tested+verified to work:
#echo off & setlocal enabledelayedexpansion
set dir=directoryyouwanttosearchin
for /f "delims=" %%a in ('dir /A:a /b %dir%') do set /A name+=1 & set file!name!=%%a
:LOOP
set /A cnt+=1
for /f "delims=" %%a in ('dir /A:a /b %dir% ^| find /C /I "!file%cnt%:~0,13!"') do set lines=%%a
if %lines%==1 del %dir%\!file%cnt%! > nul
if %cnt% NEQ %name% Goto :LOOP
exit /b
That's 9 lines :).