I'm fairly new to batch scripting, and I need to write a pretty simple .bat file that will loop through a directory. I know how to do it pretty easily using the goto command:
#echo off
:BEGIN
::set variable to data in first file
::do operations on file...
IF ::another file exists in the directory
::increment to next file
GOTO BEGIN
ELSE
GOTO END
:END
cls
The problem is that's the only way I can think of to do it. I know goto's are generally very frowned upon to use, so I was wondering if anyone knows another way to do this? Thanks!
Replace the echo.... with your desired command.
From the Command prompt:
for /R %A in (*.*) do echo.%A
In a bat file
for /R %%A in (*.*) do echo.%%A
It can be done just for the current folder too. The %%a metavariable is case sensitive and I choose to use lower case. The script will exit and quit when all files are processed.
#echo off
for %%a in (*.txt) do (
type "%%a"
pause
)
Related
I create a lot of hardlinks every week. When time comes to clean them, I find myself using the "DeleteAllHardlinks.bat" for ln (https://schinagl.priv.at/nt/ln/ln.html) but I have to drag and drop everyfile one after the other.
I would love to find a way to just select 100 files and drop them on the .bat, wait a while and find all those files and hardlinks deleted for good. Is there anyway to change the .bat file to allow this? (or maybe any other different method to acomplish the same?)
#echo off
REM
REM Check for commandline args
REM
if "[%~1]" == "[]" goto error
set LN=ln.exe
REM
REM List hardlink sibblings and delete all siblings
REM
for /f "delims=" %%a in ('#%LN% --list "%~1"') do (
del /f "%%a"
)
goto ausmausraus
:error
echo DeleteAllHardlinks: Argument is missing. Usage DeleteAllHardlinks ^<filename^>
echo e.g. DeleteAllHardlinks c:\data\myfile.txt
:ausmausraus
echo on
Thanks in advance!
Big thanks to Mofi!
The batch file could be very easily modified to support not just first argument, but all file name argument strings passed to the batch file by using one more for loop and %* as explained by call /?, i.e. use as replacement for the existing for loop:
for %%I in (%*) do for /F "delims=" %%J in ('ln.exe --list "%%~I" 2^>nul') do del /F "%%~J"
But the application starting the batch file has to pass each file name enclosed in double quotes to work properly.
Just using the for as offered in the comment solved the issue perfectly.
I am trying to rename every image in a directory to add the date that each file was created, however, I keep either getting "invalid syntax" or "A duplicate file name exists, or the file cannot be found"
I am running Windows 10, and accessing the images off a flash drive (hence the short file path). I tried having all the code in one for-loop, when that didn't work I tried using batch functions, no dice. I did see someone mention on another thread to use delayed expansion, I would be up for using this if someone could give a better explanation than the /? command.
#echo off
REM batch file is placed in top of F drive, same as "images 2017+"
cd "F:\images 2017+"
FOR /R "F:\images 2017+" %%F in (*.jpg) do call :renER "%%~nF" "%%~tF"
goto :eof
:renER
cd "F:\images 2017+"
pause
echo %1
echo %2
rename %1.jpg %1_%2.jpg
pause
goto :eof
:end
For every .jpg file in "images 2017+", the date which that file was created would be stuck onto the end after a space.
thisIsMyFile.jpg made at 5-13-2017, would become thisIsMyFile 5-13-2017.jpg
Current output
EDIT:
I am CDing into the same directory as the images are, then using the passed variables to locate the correct image (The date is one of the passed variables, and shows up in the echo command).
I notice that you only want the date, not the time so you can do that as follows using your existing Call to a label, There is also no need to use FOR /R in this case so I'll use a normal for loop:
#echo off
FOR %%A IN ("F:\images 2017+\*.jpg") DO (
CALL :RenER "%%~fA" %%~tA
)
GOTO :eof
:RenER
PAUSE
ECHO %1
ECHO %2
SET "_tmp=%~2"
SET "_tmp=%tmp:/=-"
REN "%~1" "%~n1_%_tmp%%~x1"
PAUSE
GOTO :eof
Notice how above we are dropping the Time off immediately by not wrapping it in quotes since you don't want that to be part of the file name.
You can also forgo the call to a label entirely without needing delayed expansion by using a second loop, as a matter of preference I think this is quite a bit cleaner!
#echo off
FOR %%A IN ("F:\images 2017+\*.jpg") DO (
FOR /F "Tokens=1-3 Delims=/ " %%a IN ('echo.%%~tA') DO (
PAUSE
ECHO.%%~fA
ECHO.%%~tA
REN "%%~fA" "%%~nA_%%a-%%b-%%c%%~xA"
PAUSE
)
)
this is nice and clean and with a minor edit we can paste it directly into the CMD Prompt which is nicer still This is because we are not using DelayedExpansion, Calling a Label, or using Temp variables so by changing the %%s to %s, we can then Paste this directly into the CMD Line which is often more convenient when doing these sorts of operations:
This Multi-line will do just fine to be pasted into CMD directly:
FOR %%A IN ("F:\images 2017+\*.jpg") DO (
FOR /F "Tokens=1-3 Delims=/ " %a IN ('echo.%~tA') DO #(
PAUSE
ECHO.%~fA
ECHO.%~tA
REN "%~fA" "%~nA_%a-%b-%c%~xA"
PAUSE
)
)
or, as a single line to paste into CMD if you prefer:
FOR %A IN ("F:\images 2017+\*.jpg") DO #( FOR /F "Tokens=1-3 Delims=/ " %a IN ('echo.%~tA') DO #( PAUSE& ECHO.%~fA& ECHO.%~tA& REN "%~fA" "%~nA_%a-%b-%c%~xA"& PAUSE ) )
no need to cd anywhere. ren takes a full path/filename for source - just the destination must be a filename only. So ... do call :renER "%%~fF" "%%~tF" is fine (no need to snip the extension and add it again later). In the subroutine reformat the time to a valid string and reassemble the destination file name:
#echo off
FOR /R "F:\images 2017+" %%F in (*.jpg) do call :renER "%%~fF" "%%~tF"
goto :eof
:renER
pause
echo %1
echo %2
set "string=%~2"
set "string=%string::=-%"
set "string=%string:/=-"
ECHO rename "%~1" "%~n1_%string%%~x1"
pause
goto :eof
:end
NOTE: I disarmed the rename command. Remove the ECHO after troubleshooting, if it works as intended.
#Stephan's answer is probably the best approach. But if you want to change directories ...
The windows shell has a working drive/volume, and on each drive/volume a current working folder. cd changes the working folder on a disk; to change the working folder on a drive (which is not the working drive) and to make that drive the working drive, you need to use cd /d, in this case cd /d "F:\images 2017+".
(A plain cd in this instance changes the working folder on F:\, but if your working folder is on C: -- as I'm guessing is the case -- it will not be changed.)
Assuming command extensions are enabled, you should also be able to use pushd and popd. pushd behaves like cd /d but also saves your previous location; popd returns you to that previous location. (And IIRC pushd will accept UNC paths.)
So at the beginning of your script, pushd "F:\images 2017+", and at the end popd.
I tend to favor pushd/popd over cd because invocations can be nested. So you can do things like
(assume working directory is C:\Users\IoCalisto):
pushd "F:\images 2017+"
(working directory is now F:\images 2017+)
pushd "Z:\images 2015-2016"
(working directory is now Z:\images 2015-2016)
popd
(working directory is now F:\images 2017+)
popd
(working directory is now C:\Users\IoCalisto)
... with this approach, your scripts will have fewer "side effects" and be more modular, or at least modularizable.
I am looking to make a batch file which will sift through a directory full of computer backups. The file format is "computername-date." Since I know the computer name is static, I need to find and take that directory so I can restore it's contents.
I never realized that for loops are so foreign from what I play with in other languages, so I find myself getting nowhere anytime soon.
REM First mount the drive that contains the backed up files
net use P: \\DC1\Shared\1Backups
REM Get the computer's name so we know what PC backup to use.
set SaveDirectory=%computername%
REM For each folder in the directory, do this when the computer name is found.
FOR /R "P:\" %%G in (%SaveDirectory%*) DO (
REM Restore files
echo found it.
REM Copy subdirectories into User Folder
mkdir P:\UGH
)
REM Dismount drive
The problem with what I have now is that when I run the code, the DO never runs. It should find that there is a folder called "INTERN-6.21.2019" by searching "INTERN*"
My impression of the for statement may be wrong. I need it to search through the P:/ Directory, not the subfolders. Compare the folder names to the SavedDirectory, then do something when they match. What am I doing wrong?
I've normally had good results with using CALL statements to invoke a subroutine rather than running items inside ( ).
The trick here is that you need to pass arguments to the the subroutine. Typically you'd pass the variable(s) you use in your FOR statement. These are referenced using %1 (or %1 %2 %3 if you have multiple things to pass) because these act like command line arguments. To exit the subroutine you then jump to EOF which then returns control to your FOR loop. After your FOR loop, don't forget to jump over your subroutine or you'll execute it again. In the example below I jump to :end, but you certainly could jump to somewhere else to do more things.
So using this methodology, your code might be like this:
set SaveDirectory=%computername%
FOR /R "P:\" %%G in (%SaveDirectory%*) DO CALL :process %%G
Goto :end
:process
REM Processing goes here
goto :end
:end
Hope this helps
This might get you going. This will find the "newest" directory. It is not clear from the question exactly what is to be copied and to where.
SET "MOSTRECENT=unknown"
FOR /F "delims=" %%d IN ('DIR /B /A:D /O:D "P:\%COMPUTERNAME%*"') DO (
SET "MOSTRECENT=%%~fd"
)
ECHO The most recent backup is "%MOSTRECENT%"
IF EXIST "MOSTRECENT" (COPY ...
This answer is based upon my understanding of what you're trying to do, copy a network directory named with the computername and a datestring, (with todays date), to another location:
#PushD "\\DC1\Shared\1Backups" || GoTo :EOF
#For /F "EOL=DTokens=1-3" %%A In (
'WMIC Path Win32_LocalTime Get Day^,Month^,Year'
)Do #For /F Tokens^=* %%D In ("%%C"
)Do #XCopy "%COMPUTERNAME%-%%B.%%A.%%D" "P:\UGH\" /H /R /K /Y>NUL 2>&1
#PopD
I believe a windows update broke my .bat file.
What this .bat file is supposed to do is update files within the folder with a date and time using nircmd.exe.
All file names start with mmddyy for reference.
Here is the .bat file code.
REM #echo on
REM setlocal enabledelayedexpansion
FOR /R C:\Users\USERNAME\Desktop\optimize %%F in (*.*) DO call :Setfiletime %%F
goto End
:Setfiletime
SET FNAME=%~n1
echo %FNAME%
SET MM=%FNAME:~0,2%
SET DD=%FNAME:~2,2%
SET YY=%FNAME:~4,2%
nircmd.exe setfiletime %1 "%DD%-%MM%-20%YY% 18:00:00" "%DD%-%MM%-20%YY%18:00:00"
:goto :eof
:End
REM endlocal
The output now shows each line before :Setfiletime, then ends with REM.
Why is this batch file no longer working and what would I need to do to fix it?
EDIT: Fixed - file was in the wrong location. The simplest solution was the best answer.
I have a directory with the following structure:
C:\Directory1\
sub1\
sub2\
sub3\
somefilename.txt
someotherfile.txt
Inside each sub*\ there are .dat files that I need to copy to another directory mirroring along the way the directory name where they were found. So if I find C:\Directory1\sub2\file.dat I would copy that into C:\mirror\sub2\file.dat and so on.
I tried several combinations of things similar to
for /R %SRC_DIR% %%f in (*.dat) do copy "%%f" %BACKUP_DIR%\%%~nf%%~xf
(please note this is just an example of code I was playing with, i know it doesn't work)
anyway, after trying to a couple of day I still don't know how to do it. Any chance of help?
Code is appreciated.
thanks!
This works for me:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set SourceDir=c:\source\dir
set TargetDir=d:\target\path
set FileMask=*.cpp
for /r "%SourceDir%" %%F in (%FileMask%) do (
call :ReplacePrefix target_path "%%~F" "%SourceDir%" "%TargetDir%"
call :CopyFile "%%~F" "!target_path!"
)
endlocal
goto :EOF
:CopyFile %1=source_path %2=target_path
mkdir %~dp2
copy %1 %2
goto :EOF
:ReplacePrefix %1=result_var_name %2=string %3=replace_what %4=replace_with
rem a question mark is prepended to ensure matching only at the beginning of the string
set rp_value=?%~2
call :DoIt "set %1=%%rp_value:?%~3=%~4%%"
goto :EOF
:DoIt %1=cmd
%~1
goto :EOF
Keep in mind though that it can break if paths contain unusual characters (such as = and some others which I can't remember now).
Use the following XCOPY command:
xcopy "c:\directory1\*.dat" "c:\mirror\" /s /v /c /y
If you do not want to see the filenames displayed on the screen add '/q' to the list of options.
The '/s' will copy files from subfolders. If the subfolders don't already exist they will be created.
The '/v' forces verification. Not necessary but it's nice to have that peace of mind.
The '/c' forces XCOPY to continue with the rest of the files if it encounters any problems - in other words, your batch file won't halt abruptly with only 'some' of your files copied. XCOPY will copy all that it can.
The '/y' suppresses prompting to overwrite an existing file.