I set up a batch (copy.bat) file in my Windows sendto Directory that is for Copying a bunch of files via rightclicking the directories and choosing sendto "copy.bat".
#echo off
setlocal enabledelayedexpansion
set cnt=0
set cur=0
:files
for /R "%~1" %%F in (*.7z, *.avi) do (set /a cnt+=1)
shift
if "%~1" NEQ "" goto :files
echo !cnt! files found for copying.
echo Processing ...
set "DEST_DIR=E:\"
:while
for /R "%~1" %%F IN (*.7z, *.avi) do (
if exist "%%F" (
set FILE_DIR=%%~dpF
set FILE_INTERMEDIATE_DIR=!%%~dpF:%%~1%=!
xcopy /E /I /Y "%%F" "%DEST_DIR%!FILE_INTERMEDIATE_DIR!"
)
set /a cur+=1
echo !cur!/!cnt! done...
)
shift
if "%~1" NEQ "" goto :while
pause
If I run the first for loop alone it counts the files.
If I run the second loop on its own it copies exatly those files the other loop is supposed to count.
However, both loops in one batch file will somehowe conflict. The Filecount works correctly but the copy process delivers an error: "The Path of is too long". Between "of" and "is" there is an additional Space. The final pause will correctly wait for button press. I assume it has to do something with the %%F variable, but renaming it in one of the loops does not help.
Please change the name of your .bat - copy is a keyword to cmd.
Your problem is that the first routine shifts out all of the parameters, so your second routine has no parameters to deal with.
The easiest way is probably:
set "DEST_DIR=E:\"
call :while %*
pause
goto :eof
:while
for /R "%~1" %%F IN (*.7z, *.avi) do (
...
if "%~1" NEQ "" goto while
goto :eof
which executes the second routine as en internal subroutine (the : in the call is required) providing that routine with a fresh copy of the parameter-list (%*)
You could also consider moving the if "~1"... to the start of the routine (just after the :while), change it to if "%~1" EQU "" goto :eof and change the final statement to goto while.
The label :eof is defined as end-of-file by cmd and should not be declared.
Related
I'm having problem with finding pst file in a homedrive. First I try finding in local disk
and it works like a charm by using these query
Select * from CIM_DataFile Where Extension = 'pst'
Now I'm trying to find a pst file in a homedrive but weren't able to extract the files using these code:
Select * from win32_mappedlogicaldisk
I hope someone can help me with this problem and thank you in advance.
Well.. I typically use the path of least resistance.
This batch should work for both...
Usage is: SomeNameHere.bat pst
You could just replace the %1 from the For loop and remove the first IF statement. Then add some commands for handling the PST files when you find them.
#echo off
:: Using call :label - within a loop will let called label run until end
:: of batch file.
:: When the end of file is reached, it goes to the next line in loop.
if "%1" equ "" goto showusage
setlocal enabledelayedexpansion
for /f "tokens=* delims=" %%a in ('dir /s /b /a /o *.%1') do (
set file=%%a
call :function0
)
endlocal
goto eof
:function0
if "%file%" neq "" goto function1
if "%file%" equ "" goto function2
goto function0
:function1
set %file%=%file:&:^&%
echo.Found %file%
::some commands here
goto eof
:function2
echo.
goto eof
:showusage
cls
Echo.You did not supply a file extension to search for.
Echo.
Echo.Please run the command like this example
Echo.
echo. %~nx0 txt
Echo.
:eof
How to find out for example if C:\Windows\something.tmp is a file or a directory?
Sometimes applications write their temporary data to a folder with an extension, and deleting a directory differs from deleting a file. So I must call a different subroutine for that.
The solution below works both for ordinary and network cases. There is much confusion and has even been heated arguments about distinguishing between files and folders. One reason is that the method familiar from the MS-DOS days (testing for the nul) is no more the valid solution to distinguish between a file and a folder in the Windows command-line. (This is getting complicated.)
#echo off & setlocal enableextensions
if "%~1"=="" (
echo Usage: %~0 [FileOrFolderName]
goto :EOF)
::
:: Testing
call :IsFolderFn "%~1" isfolder_
call :IsFileFn "%~1" isfile_
echo "%~f1" isfile_=%isfile_% isfolder_=%isfolder_%
endlocal & goto :EOF
::
:: Is it a folder
:: First the potential case of the root requires special treatment
:IsFolderFn
setlocal
if /i "%~d1"=="%~1" if exist "%~d1\" (
set return_=true& goto _ExitIsFolderFn)
if /i "%~d1\"=="%~1" if exist "%~d1" (
set return_=true& goto _ExitIsFolderFn)
set return_=
dir /a:d "%~1" 2>&1|find "<DIR>">nul
if %errorlevel% EQU 0 set return_=true
:_ExitIsFolderFn
endlocal & set "%2=%return_%" & goto :EOF
::
:: Is it just a file
:IsFileFn
setlocal
set return_=
if not exist "%~1" goto _ExitIsFileFn
call :IsFolderFn "%~1" isfold_
if defined isfold_ goto _ExitIsFileFn
dir /a:-d "%~1" 2>&1 > nul
if %errorlevel% EQU 0 set return_=true
:_ExitIsFileFn
endlocal & set "%2=%return_%" & goto :EOF
Originally from http://www.netikka.net/tsneti/info/tscmd075.htm
How to test if a file is a directory in a batch script?
In short :
FOR %%i IN (%VAR%) DO IF EXIST %%~si\NUL ECHO It's a directory
But all credits go to Dave Webb
You can used dir /a-d to tell you
if I check errorlevel it tells me
With a file
C:\Users\preet>echo. > something.tmp
C:\Users\preet>dir /a-d something.tmp > nul & echo %errorlevel%
1
C:\Users\preet>del something.tmp
with a directory
C:\Users\preet>md something.tmp
C:\Users\preet>dir /a-d something.tmp > nul & echo %errorlevel%
File Not Found
0
I have the following code to determine if a file has a string on the second line (::echo1, ::echo2 and ::echo3). However, when I run my code for some reason in the subroutine :_verify in the IF command inside the FOR command it completely skips the else section of IF that increases the variable %chk% and returns to :_verify, which in turn increases the number of ::echo that it is looking for in the file and searches the same file again (it needs to search each file for all 3). I tried reversing the IF command to a IF NOT and switching the else to be in front and the call :_redeem last but the same error happened. (Note: It did complete call :_verify correctly for the file containing ::echo1). It only checks each file for ::echo1 because it doesn't increase %chk% and go to :_verify again. Instead, it goes directly to goto :eof and returns to :identify to find another file that it would again, only process for ::echo1. I have added comments to help explain my script.
set gt1=1
setLocal EnableDelayedExpansion
::Identifies all files meeting the criteria (name being tmp*.tmp) and sets cap%gt1% equal the filename. Also checks to see if there are no files left (if the filename doesn't exist (i.e. it's blank because there are none left)).
:identify
set chk=1
if %gt1%==4 goto :restore
for %%A in (tmp*.tmp) do (set cap%gt1%=%%A) & call :_verify
if not exist !cap%gt1%! goto :error
goto :identify
:_verify
::Verifies that the specific file it's looking at (set as cap%gt1% in :identify) has the string ::echo1, 2 or 3 as the second line.
if %chk%==4 call :_reserve & goto :eof
for /f "skip=1" %%B in (!cap%gt1%!) do if %%B==::echo%chk% (call :_redeem) else (set /a chk=%chk%+1) & (goto :_verify)
goto :eof
:_redeem
::Renames files that are confirmed to have the string to their string name (minus the ::).
ren !cap%gt1%! echo%chk%.tmp
set /a gt1=%gt1%+1
goto :eof
:_reserve
::Subroutine used to temporairly discard files that do not meet the requirements so they will not be processed again in :identify during loopback.
if not exist temp50 mkdir temp50
move !cap%gt1%! temp50
goto :eof
:restore
::Restores files that were put in :_reserve to their previous location.
if exist %~dp0\temp50 cd temp50 & for %%C in (tmp*.tmp) do move %%C %~dp0 & cd .. & rmdir temp50
pause
:error
::Error in case it can't find all three files containing the strings.
echo Unable to find program files. Please reinstall.
echo.
pause
quit
Instead, it goes directly to goto :eof
This is the case, as you write your code to do so.
You want to write
if %chk%==4 (call :_reserve & goto :eof)
But your code works as
if %chk%==4 call :_reserve
goto :eof
You should avoid using the & separator, better use multiple lines with parenthesis.
for %%A in (tmp*.tmp) do (
set cap%gt1%=%%A
call :_verify
)
....
if %chk%==4 (
call :_reserve
goto :eof
)
i am writing a batch script monotonic file renamer. basically, it makes the titles of all the files 1 2 3 4 .... and so on. i have since expanded it to be able to handle files of different types (txt, doc, flv, etc) but not everything is working out.
my main concern is i have broken the delayed expansion calls i was making before. now using !var1! is never expanded, or never recognized as a variable.
here is a verbosely commented version of my script
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
SET tempfile=temp.txt
SET exttemp=exttemp.txt
if [%1] == [] goto usage
::make sure your dont overwrite something useful
if EXIST %tempfile% (
ECHO Temp file already exists, are you sure you want to delete?
del /P %tempfile%
)
if EXIST %exttemp% (
ECHO EXT Temp file already exists, are you sure you want to delete?
del /P %exttemp%
)
::initialize
SET /a counter=0
SET type=
SET /a ender=%1
::write filenames to tempfile
DIR /B /ON > %tempfile%
::read lines one by one
for /f "usebackq delims=" %%a in (%tempfile%) do (
REM make sure we do not rename any of the working files
if NOT "%%a"=="renamer.bat" (
if NOT "%%a"=="temp.txt" (
if NOT "%%a"=="exttostr.bat" (
SET /a counter+=1
REM get file extension
exttostr %%a > %exttemp%
SET /P type= < %exttemp%
REM housekeeping
del /F %exttemp%
REM rename
ren %%a !counter!.!type!
ECHO Renamed "%%a" to "!counter!.!type!"
)))
REM exit when we have run enough
if "!counter!"=="!ender!" goto exit
)
goto exit
:usage
echo Usage: renamer NUMFILES
:exit
::final housekeeping
DEL temp.txt
the idea is i drop my two files, renamer.bat(this file) and exttostr.bat(helper to get the file extension) into the folder and run it, it will rename files sorted alphabetically from 1 to how ever many files i specify.
when i run the code, it never uses the variables marked for delayed expansion appropriately, always leaving them as "!varname!", so it renames the first file "!counter!.!type!" and throws errors for the rest because there is already a file in the directory with that name.
this brings me to a secondary issue. sorting the dir list alphabetically results in a poor handling of numbered files. for example the list:
"1 7 15 75 120"
is sorted:
"1 120 15 7 75"
i have not been able to find a way around this yet, only that it is indeed the intended result of the dir sort. the only workaround i have is padding numbers with enough zeroes in the front.
thanks in advance for any insight!
everything is sorted but the second problem. i think i have not spoken well. i have this issue when i take IN the directory file names, not when writing out. so they already need to be padded. i has hoping there was some other way to read the directory and have it be sorted appropriately.
the most promising thing i have found is here: http://www.dostips.com/DtCodeBatchFiles.php#Batch.SortTextWithNumbers
#ECHO OFF
if "%~1"=="/?" (
echo.Sorts text by handling first number in line as number not text
echo.
echo.%~n0 [n]
echo.
echo. n Specifies the character number, n, to
echo. begin each comparison. 3 indicates that
echo. each comparison should begin at the 3rd
echo. character in each line. Lines with fewer
echo. than n characters collate before other lines.
echo. By default comparisons start at the first
echo. character in each line.
echo.
echo.Description:
echo. 'abc10def3' is bigger than 'abc9def4' because
echo. first number in first string is 10
echo. first number in second string is 9
echo. whereas normal text compare returns
echo. 'abc10def3' smaller than 'abc9def4'
echo.
echo.Example:
echo. To sort a directory pipe the output of the dir
echo. command into %~n0 like this:
echo. dir /b^|%~n0
echo.
echo.Source: http://www.dostips.com
goto:EOF
)
if "%~1" NEQ "~" (
for /f "tokens=1,* delims=," %%a in ('"%~f0 ~ %*|sort"') do echo.%%b
goto:EOF
)
SETLOCAL ENABLEDELAYEDEXPANSION
set /a n=%~2+0
for /f "tokens=1,* delims=]" %%A in ('"find /n /v """') do (
set f=,%%B
(
set f0=!f:~0,%n%!
set f0=!f0:~1!
rem call call set f=,%%%%f:*%%f0%%=%%%%
set f=,!f:~%n%!
)
for /f "delims=1234567890" %%b in ("!f!") do (
set f1=%%b
set f1=!f1:~1!
call set f=0%%f:*%%b=%%
)
for /f "delims=abcdefghijklmnopqrstuwwxyzABCDEFGHIJKLMNOPQRSTUWWXYZ~`##$*_-+=:;',.?/\ " %%b in ("!f!") do (
set f2=00000000000000000000%%b
set f2=!f2:~-20!
call set f=%%f:*%%b=%%
)
echo.!f1!!f2!!f!,%%B
rem echo.-!f0!*!f1!*!f2!*!f!*%%a>&2
)
this code can sort the filenames with one number in them (i.e. video100.mov is fine, video100video10.mov would break it)
the issue i have is i think adding a call to this helper fn will break it again, so i will be trying to include this in my modified renamer.bat now. any help is appreciated.
Probably the batch for extracting the extension reset the local environment.
But, you don't need it. You may extract the extension with the ~x option. Something similar to this ....
:monotonicrename
set /a counter = 0
for %%a in (%1\*.*) do (
if exist %%~fa (
set /a counter += 1
echo ren %%~fa !counter!%%~xa
)
)
goto :eof
to include leading zeroes in the counter, so that the directory sorts correctly, replace the previous rename command with three lines
set zcounter=0000!counter!
set zcounter=!zcounter:~-4!
echo ren %%~fa !counter!%%~xa
So putting all pieces together, add the monotonicrename function you just created in the batch file that can be as simpler as...
#echo off
setlocal enabledelayedexpansion
call :monotonicrename %1
goto :eof
:monotonicrename
set /a counter = 0
for %%a in (%1\*.*) do (
if exist %%~fa (
set /a counter += 1
set zcounter=0000!counter!
set zcounter=!zcounter:~-4!
echo ren %%~fa !zcounter!%%~xa
)
)
goto :eof
I didn't experience any issues with delayed expansion, everything worked fine for me (except, of course, for the fact that I didn't have the exttostr.bat helper script.)
Anyway, there are several things that could be improved about your script:
You don't need to store the result of DIR into a file to read it afterwards. You can read the output directly in the FOR loop.
You don't need the helper batch script. The extension can be extracted from %%a by using the ~x modifier with the loop variable: %%~xa. You can read more about modifiers by issuing HELP FOR from the command prompt.
The renamer batch file's own name can be referenced in the script as %0. You can apply the ~n modifier where you only need to use the name without the extension. The combined modifier of ~nx will give you the name with the extension.
So, here's how your script might look like with the above issues addressed:
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
IF [%1] == [] GOTO usage
::initialize
SET /A counter=0
SET type=
SET /A ender=%1
::read lines one by one
FOR /F "usebackq delims=" %%a IN (`DIR /B /ON`) DO (
REM make sure we do not rename any of the working files
IF NOT "%%~a"=="%~nx0" (
SET /A counter+=1
RENAME "%%~a" "!counter!%%~xa"
ECHO Renamed "%%~a" to "!counter!%%~xa"
)
REM exit when we have run enough
IF "!counter!"=="!ender!" GOTO :EOF
)
GOTO :EOF
:usage
ECHO Usage: %~n0 NUMFILES
As for your secondary issue, it can be easily resolved like this:
Use something like 100000 as counter's initial value. (Use however many 0s you like, but possibly no more than nine.) Add the same value to ender as well.
When renaming files, instead of !counter! use the expression that removes the first character (the 1): !counter:~1! (in fact, this is not about removal, but about extracting a substring starting from the offset of 1, learn more about it with the HELP SET command).
Here's the modified version of the above script:
::a monotonic file renamer
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
IF [%1] == [] GOTO usage
::initialize
SET /A counter=1000
SET type=
SET /A ender=%1
SET /A ender+=counter
::read lines one by one
FOR /F "usebackq delims=" %%a IN (`DIR /B /ON`) DO (
REM make sure we do not rename any of the working files
IF NOT "%%~a"=="%~nx0" (
SET /A counter+=1
RENAME "%%~a" "!counter:~1!%%~xa"
ECHO Renamed "%%~a" to "!counter:~1!%%~xa"
)
REM exit when we have run enough
IF "!counter!"=="!ender!" GOTO :EOF
)
GOTO :EOF
:usage
ECHO Usage: renamer NUMFILES
You can also see that I made some other enhancements, like making sure the file name is enclosed in double quotes, and using GOTO :EOF instead of GOTO exit (:EOF is a special pre-defined label that points at the end of the batch script so you don't need to define your own).
I've got three files in a directory that appear via another process:
c:\result\results-a.txt
c:\result\results-b.txt
c:\result\results-c.txt
Each time they all appear, I'd like to copy them to another directory with an increasing numerical suffix/prefix, once the files are copied they can be deleted. Every time the batch file starts, it can start with the number 0 (it doesn't have to scan the target directory and continue).
Ex. The first time the files all appear, the target directory might look like this:
c:\archive\results-a.0000.txt
c:\archive\results-b.0000.txt
c:\archive\results-c.0000.txt
The second time they appear, the target directory would then contain:
c:\archive\results-a.0000.txt
c:\archive\results-b.0000.txt
c:\archive\results-c.0000.txt
c:\archive\results-a.0001.txt
c:\archive\results-b.0001.txt
c:\archive\results-c.0001.txt
And so on. I'm comfortable piecing this together in a BASH enviroment, but my client requires this be done on a Windows NT (Windows 7, actually) machine. Could someone get me started?
[Edit - Answer]
Thanks to Joey below, this is what I ended up coding.
#echo off
setlocal enabledelayedexpansion
set Counter=0
:loop
call :test_file %1\results1.txt
call :test_file %1\results2.txt
call :test_file %1\results3.txt
timeout 2 /nobreak >nul
call :movefiles
timeout 2 /nobreak >nul
goto loop
:test_file
timeout 2 /nobreak >nul
if not exist %1 goto :test_file
goto :eof
:lz
set LZ=000%Counter%
set LZ=%LZ:~-4%
goto :eof
:movefiles
for %%f in (C:\test\*.txt) do (
call :lz
move "%%f" "c:\tmp\c-!LZ!-%%~nxf"
)
set /a Counter+=1
goto :eof
A very nice introduction to batch programming. Thanks.
You need a few pieces for this to work.
First of all, a counter:
set Counter=0
Then a subroutine that pads the value with leading zeroes:
:lz
set LZ=000%Counter%
set LZ=%LZ:~-4%
goto :eof
The %LZ:~-4% is a substring operation that retains the last four characters of the variable value. In this case this is a number, zero-padded to four places.
A loop that checks for files in a certain location:
:loop
if exist c:\result\*.txt call :movefiles
timeout 2 /nobreak >nul
goto loop
Fairly readable, this one, I guess.
A subroutine that moves the files away:
:movefiles
setlocal enabledelayedexpansion
for %%f in (C:\result\*.txt) do (
rem Generate the zero-padded number
call :lz
move "%%f" "some\target\directory\%%~nf.!LZ!%%~xf"
)
endlocal
rem Increment the counter for next use
set /a Counter+=1
goto :eof
Piecing all that together leaves you with
#echo off
setlocal enabledelayedexpansion
set Counter=0
:loop
if exist c:\result\*.txt call :movefiles
timeout 2 /nobreak >nul
goto loop
:lz
set LZ=000%Counter%
set LZ=%LZ:~-4%
goto :eof
:movefiles
for %%f in (C:\result\*.txt) do (
call :lz
move "%%f" "some\target\directory\%%~nf.!LZ!%%~xf"
)
set /a Counter+=1
goto :eof
It can be adapted to remember its last value. However, this will only work if the batch file resides in a writable location.
#echo off
setlocal enabledelayedexpansion
set Counter=0
call :init
:loop
if exist c:\result\*.txt call :movefiles
timeout 2 /nobreak >nul
goto loop
:lz
set LZ=000%Counter%
set LZ=%LZ:~-4%
goto :eof
:movefiles
for %%f in (C:\result\*.txt) do (
call :lz
move "%%f" "some\target\directory\%%~nf.!LZ!%%~xf"
)
set /a Counter+=1
>>%~dpnx0 echo set Counter=%Counter%
goto :eof
:init
Note that the last line (:init) must be terminated with a line break (or better two; I had some issues sometimes with just one in my testing here). This essentially creates a subroutine at the end of the batch file that sets the counter repeatedly until it arrives at its last value.
It isn't exactly fast, though. There will be one set call per counter increment at the end, and all those will be run initially.
Here's something that should get you started. Drop this in a file called incrementName.bat and then run it several times in succession.
#echo off
goto :start
--------------------------------------------
incrementName.bat
shows how to generate a filename with a monotonically
increasing numeric portion.
Run this several times in succession to see it in action.
Mon, 18 Apr 2011 12:51
--------------------------------------------
:START
setlocal enabledelayedexpansion
call :GETNEXTFILENAME rammalamma.txt
echo Next: %nextname%
#REM copy self to that new name.
copy %0 %nextname%
GOTO END
--------------------------------------------
:GETNEXTFILENAME
#REM this is a subroutine.
#REM %1 is the basename. This logic assumes a 3-character
#REM filename extension.
set fname=%1
set ext=%fname:~-3%
set base=%fname:~0,-4%
set idx=0
:toploop1
#set NUM=00000!idx!
set nextname=%base%.!NUM:~-5!.%ext%
if EXIST !nextname! (
if !idx! GTR 99999 goto:FAIL
set /a idx=!idx! + 1
goto:toploop1
)
)
:Success
goto:EOF
:Fail - overflow
set nextname=%base%.xxxxx.%ext%
goto:EOF
--------------------------------------------
:END
endlocal