I have a batch file that I call via SQL server agent.
Currently I have:
ForFiles /p "C:\folder\subfolder" /d -7 /c "cmd /c del #file"
The command works fine if there is a file older than 7days.
If there isn't a file that meets the criteria then the script fails causing SQL to report failure.
Ideally I need to add and if statement that if a file exists that is older than 7 days then carry out the delete command otherwise ignore it.
Any guidance?
Wrap it inside a FOR /F command an redirect standard error to NUL.
FOR /F "delims=" %%G IN ('forfiles /P "C:\folder\subfolder" /d -7 2^>nul') do DEL "%%~G"
Try this:
cd /d C:\folder\subfolder
SET _CmdResult=NONE
FOR /F "tokens=*" %%a IN ('forfiles -p "C:\folder\subfolder" -s -m *.* -d -7 -c "cmd /c del #file" 2^>^&1 ^| FINDSTR ERROR') DO SET _CmdResult=%%a
IF "%_CmdResult%" == "ERROR: No files found with the specified search criteria." (
SET errorlevel=0
) ELSE (
SET errorlevel=1
)
IF "%_CmdResult%" == "NONE" SET errorlevel=0
it checks for an error, and will put the error into a variable and then we set the errorlevel to zero if found.
Related
This is a sample code that allows me to delete all folders with the name ".RemoveAsap" attached to them
#echo on
set dir="\\TestPC2\c$\Users"
FOR /D /R %dir% %%X IN (*.RemoveAsap) DO RMDIR /S /Q "%%X"
pause
exit
Simply running the code as is runs perfectly but when I try to make the code more interactive, I get the error
#echo on
cd C:\Users\User1\Desktop\Test\
TYPE con >> LowDASD.txt
For /F %%A in (LowDASD.txt) do echo "\\%%A\c$\users\" >> LowDASD2.txt
set "LwDs"="LowDASD2.txt"
FOR /D /R "%LwDs%" %%X IN (*.RemoveAsap) DO RMDIR /S /Q "%%X"
pause
LowDASD2.txt would be the address/ directory location where the directories will be deleted, IE \\TestPC2\c$\Users
The code does not delete anything or give an error that "the path is too long" at least it was doing that with the previous variations that I was trying. If someone can help me with this, i would greatly appreciate it.
Try using FORFILES, instead of the command FOR, this way you can make it work like this:
:: forfiles /p "folder_location" 'args' '/c "cmd /c del /f /q #path"'
:: So...
cd C:\Users\User1\Desktop\Test\
TYPE con >> LowDASD.txt
For /F %%A in (LowDASD.txt) do echo "\\%%A\c$\users\" >> LowDASD2.txt
set "LwDs=LowDASD2.txt"
forfiles /p %LwDs% /s /c "cmd /c del /f /q #path"
:: You can use '/d -90' to delete files older than 90 days in the folder
FOR /D /R "%LwDs%" %%X IN (*.RemoveAsap) DO RMDIR /S /Q "%%X"
Simply will not work, as %LwDs% is a filename.
FOR /F "usebackq delims=" %%j in ("%LwDs%") do FOR /D /R "%%j" %%X IN (*.RemoveAsap) DO RMDIR /S /Q "%%X"
You would think might work - %%j being assigned to each entry in the %LwDs% file in turn; usebackq used because the filename is "quoted" (see for /? from the prompt for documentation)
But it doesn't - the for /d /r syntax doesn't accept metavariables...
So - try
FOR /F "usebackq delims=" %%j in ("%LwDs%") do set "target=%%j"&call :expunge
Where expunge is an internal subroutine. The colon is required
:expunge
echo target="%target%"
FOR /d /r "%target%" %%X IN (*.RemoveAsap) DO echo RMDIR /S /Q "%%X"
echo ====================
goto :eof
An internal subroutine should be placed after an unconditional goto, which should in your case follow the pause
pause
goto :eof
Where :eof (compulsory colon again) is defined as the physical end-of-file and should not be used as a user-label. Reaching physical end-of-file returns from a call.
Always verify against a test directory before applying to real data.
Note that the rmdir is merely being echoed for testing purposes - remove the echo keyword after testing to activate.
=== Extension
The full code should thus be
#echo on
set dir="\\TestPC2\c$\Users"
FOR /F "usebackq delims=" %%j in ("%LwDs%") do set "target=%%j"&call :expunge
pause
exit
:expunge
echo target="%target%"
FOR /d /r "%target%" %%X IN (*.RemoveAsap) DO echo RMDIR /S /Q "%%X"
echo ====================
goto :eof
How do I change the path of a mapped drive via batch file?
I have written a batch file to delete files in various locations that are older than 2 weeks old.
net use Y: \\servername1\c$
cd /d Y:\Reports
SET _CmdResult=NONE
FOR /F "tokens=*" %%a IN ('forfiles -p "Y:\Reports" -s -m *.* -d -14 -c "cmd /c del #file" 2^>^&1 ^| FINDSTR ERROR') DO SET _CmdResult=%%a
IF "%_CmdResult%" == "ERROR: No files found with the specified search criteria." (
SET errorlevel=0
) ELSE (
SET errorlevel=1
)
IF "%_CmdResult%" == "NONE" SET errorlevel=0
net use Y: /delete /y
timeout 10
net use Y: \\servername2\c$
cd /d Y:\Reports
SET _CmdResult=NONE
FOR /F "tokens=*" %%a IN ('forfiles -p "Y:\Reports" -s -m *.* -d -14 -c "cmd /c del #file" 2^>^&1 ^| FINDSTR ERROR') DO SET _CmdResult=%%a
IF "%_CmdResult%" == "ERROR: No files found with the specified search criteria." (
SET errorlevel=0
) ELSE (
SET errorlevel=1
)
IF "%_CmdResult%" == "NONE" SET errorlevel=0
net use Y: /delete /y
The problem I have is that after the first instance of net use Y: /delete /y it closes the console down. Without the /y the console just waits for confirmation so never moves on.
I have tried removing the first instance of net use Y: /delete /y from the script hoping the next command net use Y: \\servername2\c$ /y would just re-assign but it errors as already in use.
I know I could map various drives at the start of the script for the specific servers but if I want to use the script to delete files from a few servers that could get messy if a drive letter is already used at some point going forward, it also seems very inefficient (more so than my script probably already is)
I also could have multiple scripts for each server location but again think it would be neater handled in 1 script.
So is there a method of changing the path of a mapped drive?
This is what I have now. It appears to be working.
I will look into the single line solution too.
I couldn't leave as comment above as there was too much text...
pushd \\servername1\c$
cd /d Reports
SET _CmdResult=NONE
FOR /F "tokens=*" %%a IN ('forfiles -p "%cd%" -s -m *.* -d -14 -c "cmd /c del #file" 2^>^&1 ^| FINDSTR ERROR') DO SET _CmdResult=%%a
IF "%_CmdResult%" == "ERROR: No files found with the specified search criteria." (
SET con1=0
) ELSE (
SET con1=1
)
IF "%_CmdResult%" == "NONE" SET con1=0
timeout 15
popd
timeout 15
pushd \\servername2\c$
cd /d Reports
SET _CmdResult=NONE
FOR /F "tokens=*" %%a IN ('forfiles -p "%cd%" -s -m *.* -d -14 -c "cmd /c del #file" 2^>^&1 ^| FINDSTR ERROR') DO SET _CmdResult=%%a
IF "%_CmdResult%" == "ERROR: No files found with the specified search criteria." (
SET con1=0
) ELSE (
SET con1=1
)
IF "%_CmdResult%" == "NONE" SET con1=0
timeout 15
popd
Thanks for all suggestions and help above
I need to check if there is any file inside the subdirectories of the current directory, which was last modified a day ago or before.
This would be pretty easy by using the following line if it worked.
FOR /D /r %%G in ("*") DO forfiles /p "%%G" /D -1 /C "SET /a exists = 1"
However there is a bug with forfiles when executing commands with it and you should call another cmd from it. (See http://ss64.com/nt/forfiles.html)
At the end it needs to be called:
FOR /D /r %%G in ("*") DO forfiles /p "%%G" /D -1 /C "cmd /c SET /a exists = 1"
The problem with this, is that "exists" would be a local variable in the other cmd session and not the current one, so I won't have it available in my script.
Raymond Chen suggests to use another FOR to actually perform the operation in: https://blogs.msdn.microsoft.com/oldnewthing/20120803-00/?p=6953
This made me write the following line:
for /f %%i in ('FOR /D /r %%G in ("*"^) DO forfiles /p %%G /D -1 /C "cmd /c echo 1"') do set exists=%%i
However in this scenario, this doesn't work as I have another FOR in between and what gets written to 'exists' is the subdirectory instead of the '1'.
How can I fix this?
Thanks in advance.
Edit:
As mentioned by aschipfl, switching the placement of both FOR commands made it work, however I'm now with the limitation that it just checks the date and does not care about the time. So a file with modified date of Sep. 15, 2016 23:59 would show up if the script is ran at Sep. 16, 2016 00:00
Why do you have to use FORFILES?
Does the /MINAGE switch of ROBOCOPY help?
This should set the %_exists% variable if there are any files older than 1 day:
#ECHO OFF
(SET _fold=%USERPROFILE%\MYDIRECTORY)
SET "_exists="
FOR /F "DELIMS=" %%I IN (
'ROBOCOPY /L /S /MINAGE:1 /NS /NC /NP /NDL /NJH /NJS "%_fold%" NULL *.*'
) DO SET/A _exists+=1
IF NOT DEFINED _exists EXIT/B
REM Commands here if a file exists
ECHO( Your command is running
TIMEOUT -1 >NUl
EXIT/B
Obviously I've put a command on line 12 just for the hell of it, you would likely change that.
I want to get the number of files modified before 10 days to a variable.
I can get number of files using
forfiles /P "D:\files" /S /D -10 | find /c /v ""
But when i try to assign it to a variable using FOR it gives error.
Command I used in FOR is
FOR /F "delims=" %i IN ('forfiles /P "D:\files" /S /D -10 | find /c /v ""') DO set today=%i
It actually works fine when I remove | find /c /v ""
FOR /F "delims=" %i IN ('forfiles /P "D:\files" /S /D -10 ^| find /c /v ""') DO set today=%i
in this case you need to escape the pipe.
Yes you can use the FIND command to count how many occurrences it finds but you don't need to. You could just use the set command to iterate a variable.
FOR /F "delims=" %%G IN ('forfiles /P "D:\files" /S /D -10') do #set /a count+=1
I'm trying to cycle through a list of users (office_id_list_%YY_MM_DD%\%%) that I have for each office (office_list.txt) and create a file when files newer that a date (%3) are found. The below code wasn't working so I decided to echo the errorlevel and found that it was always -1073741510 (working on a Windows 2003 machine). Ultimately, I'm trying to identify user home directories(organized by office) that have not been modified since a given date.
Any thoughts would be greatly appreciated.
for /f "tokens=1 delims= " %%i in (U:\sysmon\u_cleanup\office_list.txt) do (
if not exist u:\sysmon\u_cleanup\results\%%i mkdir u:\sysmon\u_cleanup\results\%%i
for /f "tokens=1 delims= " %%j in (U:\sysmon\u_cleanup\results\office_lists_%YY_MM_DD%\%%i_dir_list_%YY_MM_DD%_final.txt) do (
forfiles /P %1%%i\%%j /S /D +%3 /C "cmd /c if %errorlevel% == 0 echo ** Do not Archive - Found files modified after %3 > U:\sysmon\u_cleanup\results\%%i\%%j_%YY_MM_DD%.txt"
)
)
Add the /V:on option to cmd and use !errorlevel! instead of %errorlevel% which turns on delayed expansion.
Probably, the main problem is the part cmd /c if %errorlevel% == 0, it expands the errorlevel before any of your commands are executed.
Normally delayed expansion is the choice, but here it doesn't work(or as Joey mentioned, with /V:on), because it is in a new cmd context.
Here you could use it this way cmd /c if %%errorlevel%% == 0, so if the complete block is parsed the first time, the part is expanded to cmd /c if %errorlevel% == 0, and this is expanded a second time, when the cmd /c is executed.
And you could beautifying the code a bit
set "officePath=U:\sysmon\u_cleanup"
set "officeDatePath=%officePath%\results\office_lists_%YY_MM_DD%"
for /f "tokens=1 delims= " %%i in ("%officePath%\office_list.txt") do (
if not exist "%officePath%\results\%%i" (
mkdir "%officePath%\results\%%i"
)
for /f "tokens=1 delims= " %%j in ("%officeDatePath%\%%i_dir_list_%YY_MM_DD%_final.txt") do (
forfiles /P %1%%i\%%j /S /D +%3 /C "cmd /c if %%errorlevel%% == 0 echo ** Do not Archive - Found files modified after %3 > %officePath%\results\%%i\%%j_%YY_MM_DD%.txt"
)
)