Forfiles loop/errorlevel issues - batch-file

I'm trying to use forfiles to check a folder for an updated .xlsx file (less than 1 day old) and then run another bat file depending on whether there was an updated file or not.
Initially I had a script that sort of did this, but some days there were multiple new .xlsx files, so the script "looped" and would run the success.bat file multiple times.
If no updated file is found, cmd will exit with an error, so I made use of that to run the fail.bat file.
This is what I have at the moment:
#echo on
set found=0
forfiles /p C:\ /m *.xlsx /d 0 /c "cmd /k set found=1"
if /i %found%==1 (
Call Success.bat
) else (
if /i %found%==0 (
Call Fail.bat
)
PAUSE
Now this works fine for when there are updated files.. i.e. it gets rid of the loop problem.
BUT I now have the problem with cmd exiting if no updated file is found and using
if ERRORLEVEL 1 call fail.bat
doesn't seem to work for me anymore...
I'm just no good with this if/else stuff with .bat files

forfiles /p c:\ /m *.xlsx /d 0 2>nul && call Success.bat || call Fail.bat
2>nul will suppress error messages, you can also add >nul to suppress standard output (any files found by forfiles). If no files exist with the extension then an errorlevel will be set - the commands following || will only occur if an error level is set.
Therefore, if no files are found with the specified extension this will call Fail.bat, and goto the end of the script. If files are found, it will call Success.bat.

Related

How to create a batch file with output to a log file

I want to create Batch file which runs from Task Scheduler
Requirements:
less than 30 days old files should move from main folder to Archive folder.
then the moved archive files should be deleted.
it should store all the log entries.
I have scripted steps 1 & 2, (from web searches), but inserting log file command is quite difficult for me, as I have no knowledge on batch scripts.
Script for steps 1 & 2:
forfiles /p "D:\Test" /m *.* /s /d -30 /c "cmd /c move #file E:\Test1"
forfiles /p "E:\Test1" /c "cmd /c del #path"
Can someone please show me how I can create log files using the above 2 commands.
The log file should store all files name which it moved & deleted, date and time, and success or failed.
It makes absolutely no sense to first move files from drive D: to drive E: which requires copying the data of each file, and then delete each file on drive E:. It is much faster to delete the files older than 29 days directly on drive D:.
The batch file below can be used for the file deletion task with logging success or error of each file deletion.
#echo off
if "%~1" == "" goto RunForFiles
del /A /F %1 2>nul
(if not exist %1 (echo %DATE% %TIME% Deleted successfully: %1) else echo %DATE% %TIME% Failed to delete file: %1)>>"E:\LogStoragePath\OldFileDeletionLog.txt"
exit /B
:RunForFiles
md "E:\LogStoragePath" 2>nul
del "E:\LogStoragePath\OldFileDeletionLog.txt" 2>nul
%SystemRoot%\System32\forfiles.exe /P "D:\Test" /M *.* /S /D -30 /C "%SystemRoot%\System32\cmd.exe /D /S /C \"if #isdir == FALSE \"%~f0\" #path\""
The batch file first checks if being call without any argument which is the case on batch file being started by the scheduled task. In this case processing the batch file continues with the lines below the label RunForFiles.
There is first created the directory (tree) to the log file without checking if that could be done successfully at all. The error message output by command MD on log file directory existing already is suppressed by redirecting it from handle STDERR to device NUL. 2>nul suppresses also the error message output on creation of directory (tree) due to other reasons like missing permissions on storage media to do that.
Next the log file existing perhaps from a previous execution is deleted with suppressing with 2>nul the error message output by DEL if the log file does not exist at all or the deletion could not be done because of missing permissions to do that (NTFS permissions, read-only attribute, log file currently opened in an application).
Then FORFILES is executed to search in
directory D:\Test specified with path option /P
and all its subdirectories because of option /S
for file system entries (directories, files and reparse points)
matching the wildcard pattern *.* (any) specified with mask option /M
with a last modification date less than or equal 30 days
to execute the command specified with option /C.
The command results in running one more Windows Command Processor with the options /D to ignore the AutoRun registry value and /S to interpret everything after next option /C as one argument string with the command line to execute and option /C to execute the command line specified next and close itself after finishing the execution.
The started Windows Command Processor process first checks with a case-sensitive string comparison if the current file system entry found by FORFILES with a last modification date older than 29 days is not a directory. A directory is ignored and results in an immediate close of started cmd.exe.
For a file is executed next by started cmd.exe the currently processed batch file with the full file name enclosed in " as argument. So the batch file processed by cmd.exe runs forfiles.exe which starts one more cmd.exe to process the same batch file, but this time with a file name as argument for the batch file.
This time the batch file processing continues on third command line which uses the command DEL with option /A (all attributes) to overrule the implicit default /A-H (all attributes except hidden attribute) to delete also a file with hidden attribute set and option /F to force a deletion of a file with read-only attribute set.
The file deletion is in most cases most likely successful. But an application having currently opened a file older than 29 days or special NTFS permissions can result in a failed file deletion.
Therefore an IF condition is used to verify if the file really does not exist anymore in which case a success message is output to handle STDOUT using command ECHO with the dynamic variables DATE and TIME. Otherwise on failed deletion of the file a failed message is output with ECHO with DATE and TIME to handle STDOUT. The output message is redirected to the specified log file and being appended on this log file which is opened, modified and closed for each file to delete.
Last the execution of the batch file to delete an old file is exited with command EXIT using option /B to just exit the batch file processing and not entire cmd.exe processing this batch file. cmd.exe started by forfiles.exe has nothing more to do and closes which results in forfiles.exe searching for next file system entry matching the specified criteria.
The entire file deletion task is horrible slow using this batch file. It would be definitely much better to use for this task a small PowerShell script processed by Windows PowerShell which would do this file deletion task much faster.
I recommend to read also the chapter Best and simple backup deletion concept in my answer on Bat file to delete files only when younger files are present. It is in general not advisable to simply delete all files older than X days without checking if younger files are present, especially if the files to delete are backup files or log files of a periodically executed task. The solution posted in referenced answer using a different deletion strategy than just last modification date is also much faster than the solution posted here.
Here is also a variant of above with first moving each old file from D:\Test and its subdirectories to E:\Test1 without replicating the directory tree in destination directory with logging success or failure of file movement and finally deleting all files in E:\Test1 recording also success or failure of file deletion. File deletion failure is very unlikely.
#echo off
if "%~1" == "" goto RunForFiles
move /Y %1 "E:\Test1\" >nul 2>nul
(if not exist %1 (echo %DATE% %TIME% Moved successfully: %1) else echo %DATE% %TIME% Failed to move file: %1)>>"E:\LogStoragePath\OldFileMoveLog.txt"
exit /B
:RunForFiles
md "E:\Test1" 2>nul
md "E:\LogStoragePath" 2>nul
del "E:\LogStoragePath\OldFileMoveLog.txt" 2>nul
%SystemRoot%\System32\forfiles.exe /P "D:\Test" /M *.* /S /D -30 /C "%SystemRoot%\System32\cmd.exe /D /S /C \"if #isdir == FALSE \"%~f0\" #path\""
set "ProcessedFile="
(echo %DATE% %TIME%
for %%I in ("E:\Test1\*") do (
set "ProcessedFile=1"
del /A /F "%%I" 2>nul
if not exist "%%I" (echo Deleted successfully: %%~nxI) else Failed to delete file: %%~nxI
))>"E:\LogStoragePath\OldFileDeletionLog.txt"
if exist "E:\LogStoragePath\OldFileDeletionLog.txt" if not defined ProcessedFile del "E:\LogStoragePath\OldFileDeletionLog.txt"
The last command line deletes the file deletion log file on being created but containing just date and time because of no file was moved before and so no file needed to be deleted at all.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
cmd /?
del /?
echo /?
exit /?
for /?
forfiles /?
goto /?
if /?
md /?
move /?
set /?
See also the Microsoft documentation about Using command redirection operators.

xcopy works from command prompt but fails in batch file using variable

I am trying to run a batch file against a list of remote computers.
The batch file will check for a specific file, if the file is not found in the directory it should be copied there.
I am using the %%a variable for the PC names.
The xcopy command fails in the batch file with invalid drive specification.
If I take the xcopy command and replace the variable with the computer name and run from a command prompt it will copy the file over.
I am not sure why the xcopy command fails in the batch file when using %%a
PC.txt is my text file with the list of computers.
I am using the hostname and not the FQDN in the text file
for /f %%a in (PCList.txt) do (
If Exist "\\%%a\c$\Program Files\Folder\App.exe" (
ECHO %%a App.exe exists
) ELSE (
ECHO %%a App.exe Does Not Exist
xcopy "C:\Folder\App.exe" "\\%%a\c$\Program Files\SubFolder\" /Q /R /Y
First of all, it looks like you have one or two syntax errors. You opened a parenthesis in ... Folder\App.exe" (and you haven't closed it since, the same thing with the ) ELSE (. Also, sometimes the parenthesis inside a FOR can cause interference with the rest of the code. If you add the missing parenthesis does not resolve, try to fix the code to: Folder\App.exe" ^( which can help.
Try this:
for /f %%a in (PCList.txt) do (
If Exist "\\%%a\c$\Program Files\Folder\App.exe" ECHO %%a App.exe exists
) ELSE (
ECHO %%a App.exe Does Not Exist
xcopy "C:\Folder\App.exe" "\\%%a\c$\Program Files\SubFolder\" /Q /R /Y )
Also, you can use the COPY only, maybe the problem is with the COPY. And using the parameter if exist to a entire .exe isn't the best idea, the user can just create a txt file and rename it as App.exe.
Hope this helps,
K.

Why does my batch file create an additional copy in current folder?

I have a folder xyz. It has a batch file which copies files from source directory abc to destination directory def. I am using copy with options /v /y.
Copy works absolutely fine. But I notice a strange or weird issue that additionally a copy of all the files copied from source to destination are present in folder xyz.
I started observing this issue after a system restart and not sure if its a one time issue. But I would like to know if someone has run into this issue before and what is the possible fix?
Here is the code:
#if not defined ECHO_ON echo off
SETLOCAL EnableDelayedExpansion
set arg1=%1
set arg2=%2
copy /v /y !arg1! !arg2!
call :getPath !arg1!
ren !arg2!\!_NAME_EXT! !_NAME!.svg
:getPath
set _NAME=%~n1
set _NAME_EXT=%~nx1
set _LOC=%~dp1
goto:eof
endlocal
Please note I am using copy and robocopy command (for some other copying operation) in same .bat file.
Is this something to be worried about?
(As I wrote things worked fine until restart.)
Your double Copy is because a batch script works line by line until it reaches an end of file marker or an exit instruction. A Call command returns back to the point just after the Call instruction. When it returns, there is no exit instruction or end of file marker until the bottom of your script, so the :getPath label is executed again.
There appears to have been absolutely no reason for EnableDelayedExpansion in your script, for Setting any variables or for a Call command. I have therefore simplified it as such:
#Echo Off
If "%~2"=="" Exit /B
If Not Exist "%~2\" MD "%~2" 2>Nul || Exit /B
If Exist "%~1" Copy /V /Y "%~1" "%~2\%~n1.svg"
I hope it helps you out.

Batch File Rd Error: The directory is not empty

I've been working on a code to delete the files from a Location inputted in .txt file.
#echo off
cd C:\Users\Troy\Desktop\Details
set /p loc=<Location.txt
rd /s /q "%loc%"
echo %loc%
pause
This code returns me the following output
The directory is not empty.
C:\Users\Troy\Downloads\TV Shows
Press any key to continue ...
Now the file Location.txt, when opened contains following
C:\Users\Troy\Downloads\TV Shows which is in accordance with the echo output I get in the second line (of the above output)
Also note that I have saved the batch file at C:\Users\Troy\Desktop
So there arises no reason for any interference due to the same location.
The weird part is when I run the following code from another batch file at the same location it runs perfectly fine and deletes all the files.
#echo off
set loc=C:\Users\Troy\Downloads\TV Shows
rd /s /q "%loc%"
echo %loc%
pause
So the only difference between the two codes is that the first one sets the variable location from a specific file, whereas the other one has a pre- inputted variable.
Also I have tried to delete files from the location using the following code
#echo off
cd C:\Users\Troy\Desktop\Details
set /p loc=<Location.txt
cd %loc%
del /s /q * >nul 2>&1
cd C:\Users\Troy\Desktop\Details
rd /s /q "%loc%"
echo %loc%
pause
In the above code, the delete command works perfectly fine and deletes all the files within. However folders and subfolders are all that are left, which means that rd command is not working
I've even tried the attrib -h thing, but that does not work either.
Also note that I've tried this with various permutations and combinations of rmdir /s /q too. But does not work.
Any help appreciated.
You could be suffering from some corruption in the file system. Try running chkdsk /f. You'll have to reboot in order to run it, but see if it finds something that it can correct, then see if your problem goes away.

"if not exist" command in batch file

I need to write some code in a windows batch file.
The interested part of this script should create a folder if this folder doesn't exist yet, but, if this folder already exists, it should NOT overwrite the content.
I tried something like this:
if not exist %USERPROFILE%\.qgis-custom (
mkdir %USERPROFILE%\.qgis-custom
xcopy %OSGEO4W_ROOT%\qgisconfig %USERPROFILE%\.qgis-custom /s /v /e
)
But I'm not sure if I'm doing it right.
Thank you
if not exist "%USERPROFILE%\.qgis-custom\" (
mkdir "%USERPROFILE%\.qgis-custom" 2>nul
if not errorlevel 1 (
xcopy "%OSGEO4W_ROOT%\qgisconfig" "%USERPROFILE%\.qgis-custom" /s /v /e
)
)
You have it almost done. The logic is correct, just some little changes.
This code checks for the existence of the folder (see the ending backslash, just to differentiate a folder from a file with the same name).
If it does not exist then it is created and creation status is checked. If a file with the same name exists or you have no rights to create the folder, it will fail.
If everyting is ok, files are copied.
All paths are quoted to avoid problems with spaces.
It can be simplified (just less code, it does not mean it is better). Another option is to always try to create the folder. If there are no errors, then copy the files
mkdir "%USERPROFILE%\.qgis-custom" 2>nul
if not errorlevel 1 (
xcopy "%OSGEO4W_ROOT%\qgisconfig" "%USERPROFILE%\.qgis-custom" /s /v /e
)
In both code samples, files are not copied if the folder is not being created during the script execution.
EDITED - As dbenham comments, the same code can be written as a single line
md "%USERPROFILE%\.qgis-custom" 2>nul && xcopy "%OSGEO4W_ROOT%\qgisconfig" "%USERPROFILE%\.qgis-custom" /s /v /e
The code after the && will only be executed if the previous command does not set errorlevel. If mkdir fails, xcopy is not executed.
When testing for directories remember that every directory contains two special files.
One is called '.' and the other '..'
. is the directory's own name while .. is the name of it's parent directory.
To avoid trailing backslash problems just test to see if the directory knows it's own name.
eg:
if not exist %temp%\buffer\. mkdir %temp%\buffer

Resources