Bat file MOVE fails due to other process handle - batch-file

I have a bat file as part of larger maintenance system that runs on a nightly basis, performs a bit of housekeeping, SVN updating etc. Part of this involves moving/deleting files, however, occasionally this fails due to another process not releasing a handle on the files/dirs to be moved. Is there any way to force the BAT file to override any existing handles and continue with the MOVE? I can only think of a look up method using ProcessExplorer/Assassin - although I'm not sure that would even work. Alternatively a "sleep" and then reattempt if it failed the first time, although that would be a matter of luck than solving the underlying problem. Any ideas/suggestions much appreciated. Thanks.

Robocopy has a move function, and can wait on error

Here are a few things that I have done in similar situations:
Before the move command, ensure that other script threads CD out of the target directory.
Use robocopy (from the Resource Kits) with options like retry /r:3 and wait /w:5.
Script the first action as COPY so the script can continue working, then later in the script do the deletion at the old/unneeded location.
As you've already mentioned, create a little retry loop using IF ERRORLEVEL commands to test the success of the MOVE command.

Related

Setting "echo on" globally for batch scripts

I have experienced (random) weird corruption of environment variables from consecutive calls of multiple (3d party) batch scripts. These individual scripts call #echo off.
Is it possible to force echo on by environment variable/other means or I need to go to the every script and comment/remove the line to see what has been actually executed?
I'm quite sure that this is not possible. You can't force your system to simply ignore a command like ECHO OFF. The only way to I can think of as a workaround is to write a script which replaces all ECHO OFF commands in you bat files before execution and restoring them afterwards.
Take a look at this: https://superuser.com/questions/351661/how-do-i-force-echo-on-for-a-batch-file-without-editing-the-file

How to refactor a Windows batch script littered with GOTOs?

I have to maintain a batch script of about 3500 lines littered with GOTO. Seems that the original "developer" hasn't heard of this famous paper and modular programming.
What the script does?
The script deals with the (silent) installation/uninstallation/reinstallation of several programs using different options. It could be split in several files that deal with each program in part. The problem is that if you're trying to take a part in another file that part will still GOTO another section that needs to be in the original script.
Refactoring?
Normally you wouldn't do a refactoring without having automated tests (so you can be sure you didn't break anything), but I don't know how to do it. There's no testing framework for that.
Partial Solution
I have come up with a partial "solution" that is some kind of adaptation of characterization tests (from Working Effectively with Legacy Code by Michael Feathers) and approval tests:
- create another script: test.py that replaces all commands (like copy or msiexec) with echo,
- redirect the output to a text file (good.txt),
- change the original batch script,
- run the test.py script again and save the output to another text file (current.txt),
- diff good.txt and current.txt -> if there are no differences then I didn't break anything, but if they are different I need to check if I broke something.
Problem with partial solution
How can I capture and replace all the commands? I could make a list of commands to replace, but there are also a lot of string concatenations to get the name and path of the program to be installed.
CMD level capture/hook?
Is there any way I can hook into the command line interpreter (CMD.exe) so I can replace on the fly all the calls to installers with echo?
Other suggestions?
Do I approach the problem in the wrong way? Can I do it better somehow? Do you have some advice I could use?
You could replace all COPY, DEL or CALL with %COPY%, %DEL% ,...
So you can use the same file for production and also for the tests.
#echo off
if not defined UNITTEST (
set "COPY=COPY"
set "DEL=DEL"
set "CALL=CALL"
)
%COPY% src dest
%DEL% somefile.txt
%CALL% installer.exe
And from your unittest.bat, you could start it via
#echo off
set "COPY=>>trace.log ECHO COPY"
set "DEL=>>trace.log ECHO DEL"
set "CALL=>>trace.log CALL ECHO "
del trace.log
set "unittest=Active"
call production.bat
fc good.txt trace.log
I'm not an expert in Batch, but I have done my fair share of it. With that said, I can offer a few tips.
Forget trying to do it all at once. Batch is very hard to debug. Echoing out to a log file helps a lot, but it will not capture everything you need if something goes wrong.
Work on breaking out the exe and msiexec calls into self-contained scripts. It is much easier to test the small script for the functionality you desire. Once you have that working, it is simple to call that script from the "Master" script.
Establish a good protocol for passing args to, and return codes from the smaller scripts. If there are common settings needed to be used for all the scripts consider using a central settings file.
GOTOs are not the devil, unless they pass control all over the place. Normally there are two good reasons that I know of to use GOTO’s.
Skip past a block of code that does not need to run.
To SET values into variables. Note there is a bug that can prevent variables from having their value set from within an 'IF' statement block. That little bug caused a big headache for me at one time.
Calls to a label might be better option at times.
Depending on how far back the legacy support is required, consider using Powershell when possible. The power and debugging capabilities of Powershell far out way the benefits of simple scripting of Batch. Which at 3500 lines simplicity has already been lost. You are already looking at Python, so maybe that could be used instead.
If you need a break point, use Pause. ECHO all the settings you need to examine right before the pause. This is as close to a break point I have found for batch.
Echo the command you intend to run to a log file and actually run it.
Write small verification scripts to be used independently or with the “Master” script to confirm you are getting the results you are expecting.
Use the right tool for the job. I like to use EditPadPro, RegexBuddy, and BeyondCompare for batch editing and comparing differences. There free tools that can be used too NotePad++ and Windiff. Making many edits in a file of that size is best handled by a good editor. IE inserting an echo at the beginning of a line that calls a cmd.exe.
Remember it is scripting not programming. While there is a lot of overlap of the two, the same exact approach to a problem may not be viable between the two.
Always make a backup copy of the scripts as a whole before mucking around. A fallback position is greatly appreciated when there is one small bug that you can’t find.
If it ain't broke... well you wouldn't be working on it if everything was working just fine.
Always test changes. And when you are done test it again. After that have someone else test it.
Just my .02. I’m sure someone else can chime in with more advanced advice. My knowledge on Batch has been acquired from the school of hard knocks, supplemented by ss64.com

one log file for several batch processes locks up

I have very basic batch file knowledge. My first script was something I found to export Oracle Discoverer reports via windows task scheduler. That's basically all I know, I've got several of them (maybe 40 or so) that run at various times, some every 30 mins. They sometimes overlap in time.
My issue is not the specific discoverer export, but the logging of errors. I want to log everything to a single log file... with excel and access processes, I can loop until free and all is good; with the discoverer batch files, the log file gets locked at the beginning and doesn't let anything else log in until done. Some of these discoverer reports may take 30 mins or more, messing up all my runs.
Here's an example of my bat file:
#echo off
echo my process %date% %time% >>c:\test.log
c:\orant\DISCVR4\DIS4USR.EXE /connect MyUserID/MyPassword#myserver /open "c:\DiscoReport.DIS" /export xls "c:\MyFile.xls" /batch 1>>c:\test.log 2>>&1
I have a bat file with several of those individual process bat files, so that they run one at a time. That works fine. But when the run takes longer than estimated, then the next run fails... because they all start by running disco, and the log file is locked throughout and until the end... Is there something I can do to just open and close it right at the time of adding the results only?
I've looked for answers, and I believe there's something that might be done with the TEE or redirecting the results maybe to null and then using that as input piped to write to the log? but I don't really know how to do this... looked, tried, weeks and weeks, can't get anything working... Pretty please, I'm sure those who know, can do this with one single line.. Pls help...
Essentially, NO - if you want the log file to contain all events in time-order.
You could have the discoverer processes create their individual logfiles and then
type discoverer.log.file >>logfile
del discoverer.log.file
which would group all of the discoverer process output together in the logfile.
Otherwise, you'd have to put up with more than one log.
I severely doubt TEE could do it as TEE would then itself need to hold the log open, so you're back at the start - But I'll emphasise I haven't tried it.

Restart batch file on crash/error

I have a batch file that encounters several errors. These errors require the command prompt to be forcefully closed. Which causes me to have to open the file again to fix the issue.
Due to the nature of this application it is required to run all the time.
I'm looking for a way to automate the file to restart when it encounters an error. Is there a command I can do this with?
Could you please describe in detail and why the command accomplishes such a resolution?
Update:
What I would recommend to accomplish your goal, would be to turn your batch into a Service. (Documentation here) By converting your batch into a Service it no longer becomes subject to users being logged in, permission issues, it will run as a SYSTEM ACCOUNT. This in itself can alleviate a lot of anger for the process.
After you've completed that, you can write a batch file that ensures that your Service is indeed running. Your current issue, is it doesn't automatically restart. Well, a Service always runs- Even if it has an error it will still attempt to run.
Which means unless it has a Fatal Exception your Service should always work- But for certainty you can create a batch that will ensure your Service is running.
An example:
:START
timeout 3600
for /F "tokens=3 delims: " %%H in ('sc query "MyServiceName" ^| findstr "
if /I "%%H" NEQ "RUNNING" (
NET START "MyServiceName"
REM Service has Started...
)
)
GOTO START
So in theory every 3,600 seconds it will test if your Service is running, if it isn't it will start the Service for you.
Important:
This is more the proper way to resolve your issue, rather then circumvent it. However, as I noted your batch should still implore Exception Handling to ensure your application doesn't fall into an unusable state. This still isn't the best way, as it should implore Exception Handling and Verification to test against it's state.
As I mentioned before, you have a lot of methods to solve your issue. However, your thinking in a Linear Mindset. Which means:
Execute Command, Goal Guaranteed.
If I do this, this happens.
Essentially based on the minimal example I saw, it looks like you've created an infinite loop to continually execute your command. My question to you: When your loop has an error, how can it continue to run?
You've already stated that it happens in random areas- Nothing is random, those are more then likely areas that require some verification / testing to ensure it remains in a proper state. The faster your identify the potential problems, the more effective your program can run with no errors.
Hopefully that helps-
What exactly does your batch application do?
The reason I ask is because you can circumvent the issue with Windows Task Scheduler which allows you to configure some parameters to auto start and auto open particular applications based on your specified criteria. Will it be ideal? Will it truly automate to your needs- More then likely not.
As mentioned above by GolezTrol, the cause of your error will be the more important aspect to resolve your issue. Based on your remark
The errors are different each time, to be honest.
That could be an indicator that the batch script doesn't adhere to testing but rather assuming it successfully completed. Without any underlining information such as:
Function
Code Example
Where an error occurred, and during what task.
It makes it relatively difficult to point you in the proper direction. One thing that I would consider is IF. This is a fundamentally basic task but is quite important-
if(Directory.Exists(dirName))
{
// Do This
}
else
{
// Do This
}
I find the C# outline an easier method to understand the purpose of the IF. You can actually implement something similar in your batch. You would accomplish it like this:
if exist { insert file name } (
rem file exists
) else (
rem file doesn't exists
)
or you can accomplish it like this:
if exists c:\myFile.bat notepad c:\myFile.bat
If C:\myFile.bat exists, then open notepad. The reason this is an important is because if the variable doesn't exists, then it can not be affected. This allows your application to essentially make decisions in a very primitive manner.
You have quite a bit of flexibility- There are a lot of examples on this topic because batch programming has been around for a very, very long time. Another alternative would be to eventually move to Powershell. It will have access to the Windows Management Interface (WMI).
Hopefully this points you in the right direction, without more information our answers may not be much help.
I believe, that easiest way is to create one more .bat file with GOTO statement:
#echo off
:startover
echo (%time%) App started.
call "c:\app.bat"
echo (%time%) WARNING: App closed or crashed, restarting.
goto startover
Possibly this will fix your problem:
http://nssm.cc/usage
Basically what it does is you adding some bat file to nssm and making it a service.
In "Action on exit" part it says:
To configure the action which nssm should take when the application exits, edit the default value of the key HKLM\System\CurrentControlSet\Services\servicename\Parameters\AppExit. If the key does not exist in the registry when nssm runs it will create it and set the value to Restart.

Need some suggestions in batch file scheduling / calling

I have a batch file which calls a Perl file which requires a input as a text file
CreateTasks.bat:
Createtask.pl -f %1
where %1 - "C:\task\TASK1234.txt"
The file TASKxxx.txt (ex : TASK1234.txt) will be create from another application and at any point the folder C:\task\' can contain one more more Tasxxx.txt files
I want to Call the CreateTasks.bat once for each file, reason is that This folder will be filled in with the text file and this folder needs to be monitored.
How can I call achieve this?
I was thinking of scheduling a Batch file which runs every 5 mins. Get always the first file and call this batch file
is this good approach?
if something is not clear please do let me know and I will try to clarify
Thanks
Karthik
You may use a BAT file that periodically loops over all the files of a given extension in a given folder, and for each such file found, it would invoke some special process and, if succesful, rename the file so it does not process it again. This simple code implements the main idea of this strategy, use it to get you started.
#ECHO OFF
SET TASKPATH=c:\temp
SET TASKEXT=tst
FOR %%A IN ("%TASKPATH%\*.%TASKEXT%") DO (
ECHO DOMYTASK "%%A"
REN "%%A" "%%~nA.DONE"
)
But, watch carefully, as there are many traps in this road; to cite some, take care with the error processing, address possible issues with cancellation of processes, stalled executions, double or multiple executions, even in parallel, and consider race conditions in all the checks you might prepare.

Resources