Do Windows .bat scripts have an exit-trap like feature, that would allow registration of cleanup actions that always need to be run?
I am not interested in any solution that uses Powershell or other shells (I already have Python installed on the target system so if that can't be done in batch files I'll just wrap a bunch of calls to subprocess.Popen in try...finally).
No, there are no exceptions, and no formal way of registering an exit routine to run automatically upon termination.
However, you can get close if you do not need environment changes to persist after the script terminates. You can run the majority of your script in a new CMD session, and then you can have cleanup routines in the parent level that will always run upon termination as long as the console window is not killed. Control will be returned back to the outer shell if the inner script terminates normally, or terminates due to Ctrl-C, or terminates due to fatal syntax error, etc.
Here is a trivial demonstration:
#echo off
if "%~1" equ ":main" (
shift /1
goto main
)
cmd /d /c "%~f0" :main %*
echo Cleanup actions go here
exit /b
:main
echo Main actions go here
exit /b
UPDATE - Hacked exceptions
I have since developed an effective exception hack using nothing but native batch commands - See Does Windows batch support exception handling?.
Exceptions can be thrown at any level of your code, and the exception bubbles up until a catch block handles the exception. The code cannot recognize errors on its own - each exception must be explicitly thrown by your code. It also cannot respond to , fatal syntax errors, or closed console. But if those limitations are acceptable, then it is an extremely effective way of installing cleanup code.
Batch doesn't support anything like this.
However, cmd also doesn't have exceptions and very few ways of abnormally terminating a script (a syntax error in your script, or crashing cmd.exe for example). If your script doesn't intentionally terminate itself, it should be safe to simply put any cleanup at the end of your script.
To go further, you can call your batch file in a subshell from another batch file, and have the outer script do your cleanup. Then, no matter what happens in the script, the outer shell will just run the next command in the outer batch when it finishes.
Related
So, I have a program for the Windows Command Prompt, which is for changing the directory (so I can go to a language directory without having to do cd everytime)
and I want to kill it after I select an option. However, I have tried some
methods, which do one of the following:
a. taskkill /IM ... (blows up with a process not found error)
and
b. exit [as shown here] (does what I want, but it also closes the command prompt)
This is my program (the important part):
:C++
cd C:\Users\S.G.\Documents\C++ Scripts
echo What's in C++ Scripts:
dir
pause
exit
:Python
cd C:\Users\S.G.\AppData\Local\Programs\Python\Python36-32
echo What's in Python:
dir
exit
The reason why I'm stuck as to how one does this is because say I choose option "C++".
If I choose it, it runs what I have described, but it also runs the "Python" function. If I run the "Python"function however, it runs fine and doesn't display whatever's in the "C++" function.
Why is function "C++" also running "Python" when I intend not to?
You should use exit /b, followed by an optional error code (eg. exit /b 0). An alternative way to do this is to skip to the end of the file using GOTO:EOF.
I have a script that calls other commands in a for loop:
for %%x in (%CMDS::= %) do (
call C:\%%x %1%
echo "%%x complete"
)
However, running this results the console spitting out :
'sleep' is not recognized as an internal or external command,
operable program or batch file.
This is because the files i loop through and run have these commands in them. Why is it that if i run these files one by one they work, but when chained using call they don't? I can sleep in my terminal outside of this script..
Regards
Thanks to another answer, I solved this error by replacing sleep 5 in my .bat file with:
powershell -Command "& {sleep 5}"
Works fine now. Better still, also tested Stephan's suggestion:
timeout 5
Simpler, and shows a nice message like
Waiting for 0 seconds, press a key to continue ...
Note that some Windows versions require the /t option to define the time.
timeout /t 5
There is no sleep command in batch. That's why you are getting this error.
EDIT:
There is no sleep command in Windows CMD or Batch. BUT: as you can use the command in your console, I suppose there might be a script or a program called sleep. This script or program might be situated in your working directory or in some other directory included in your %PATH% variable. If this is the case, it's possible that your script gives you this error because of a path issue.
Say, you are in C:\SomeFolder and there is a sleep.exe in there. You are calling another script or command which changes the current directory to D:\AnotherFolder. Now another script or command tries to execute your mysterious sleep command assuming the working dir to be C:\SomeFolder but as you are in a different folder (D:\SnotherFolder) now, sleep can't be found. Further, when using call the variable scope of the calling script becomes also the scope for the called script. So it's also possible that variables are being overwritten by different scripts. Such a variable might contain the path to your sleep command. This could also cause an error.
I have a batch script which mount's a .vhd file and creates a junction point, the it call's another batch script which runs some things on the mounted vhd/junction point. When that script finishes, it then comes back to the 1st batch script and deletes junction point, unmounts .vhd then exits.
The problem is the second script needs to exit properly in order to run the rest of the first script, when most of the users will just ctrl+c and close the second script.
Is it possible to prevent this from happening, either by not allowing the ctrl+c cancel or somehow restart the first batch file after the call has happened?
try this to start the 2nd batch file:
start /b /w "" 2nd-batch.bat
Thanks to Endoro's answer, even though it didn't work for me, it made me remember start, i always forget start and just use call. anyway the solution i found was was to do this:
2>nul (
echo N|start /wait "" cmd /c C:\2nd-batch.BAT
)
it creates a new window for the 2nd-batch.bat which allows the dumbuser to even exit it via the X button and it still completes the rest of the 1st-batch.bat
This is sort of a follow-up to my question earlier (link).
To test things out I made this simple batch file to ensure the Task Scheduler was properly executing the batch file:
cd "C:\Users\user\Desktop"
echo. 2>test.txt
So after the test.txt document is created on the desktop, the batch file should end but it continues to run:
Is there a way, either at the end of the batch file or a setting in the Task's Properties, to ensure that the cmd process quits?
Thanks!
I ran into the exact same problem. However, I felt duped when I read what Trevor778 wrote in this post:
I had the same problem - the task worked but the status kept showing Running. One simple thing to try is click on the Task Scheduler Library in the left column. Click Action/Refresh. Presto. Status changed to Ready. That's all it was for me, the task ran fine, just the status didn't update. Hope this helps.
ref: https://social.technet.microsoft.com/Forums/en-US/2f6dc29c-3b8b-45f5-a2a7-53e076acc062/task-scheduler-scheduler-status-is-being-running-always?forum=winservergen
you can add "exit" to last line of your script
cd "C:\Users\user\Desktop"
echo. 2>test.txt
exit
Running TASKKILL /F /IM cmd.exe will kill all cmd.exe processes whether it was the one that spawned this batch file or not. That's probably not desirable behavior. :-)
Judging by your last question, I'm guessing you're still running your task with cmd.exe /k, which will keep that window open indefinitely. For an unattended task, cmd.exe /c is a better choice. When the batch file finishes, the process should end.
Same here on Windows 7.
Putting all batch files in a directory in the user User specific path who runs the task
run programm = " cmd.exe " (without a path)
Your extras, mine where = " /c "C:\Users[username]\whatever\your_batchfile.bat" >> log.txt" "
" >> log.txt " so that i can see the output of the batch...
start in = " C:\Users[username]\whatever "
I also checked the "run with highest privilges" box
after that everything worked fine :)
Use following
exit /B
you may find more information in windows console area then type:exit/?
I know it's an old question, but I personally found that if I let a pause at the end of the bat file, it would keep the status as "Running".
I usually leave a pause at the end to help with debugging, but I found when I removed it, the task scheduler finally recognised it as having exited. It didn't help if I just refreshed it.
The solution I found was to add this line at the very end of the batch file:
TASKKILL /F /IM cmd.exe
Now after the batch file task runs and completes, it is no longer in the All Running Tasks list and the status goes back to 'Ready' instead of staying at 'Running'.
Warning:
That command will kill all running command processor instances so it may be potentially harmful!
I understand how to call nested batch files from within a parent file using the call command, as there are plenty of resources on that:
CALL
CALL (SS64)
Bat file termination
However, I don't understand why calling another batch file from another terminates the parent.
For a less abstract example, suppose I have a batch file that "links" together separate batch files, and I erroneously didn't prepend call to each line:
foo.bat
bar.bat
This would only execute foo.bat and then exit. To correctly execute both commands, I would have to prepend call before each statement:
call foo.bat
call bar.bat
Why does the first functionality still exist? Why hasn't it been changed? I noticed that call was introduced in MS-DOS 3.3, which was released in the late 1980s, so is this functionality still here for reverse compatibility?
I can't think of any (practical) usages of it, but perhaps I'm too used to "new" programming techniques.
DOS used simple text processing (back when you had things like FILES=20 in config.sys to allow 20 file handles), so opened the file, read the next line, closed the file, then executed the line just read. If the file called another, then the processing continued with that file, so only 1 file handle would be required for a batch file.
Until Microsoft put in the call command, there was no way to get back to the original file (without using tricks like giving the name of the previous file as a parameter, and using temporary files to let the original batch file know it had dome some processing, and could then GOTO the next part of the file).
As Sean Cheshire wrote, it's necessary for backward compatibility.
But starting a batch file from a batch file without using CALL does not terminate the parent!
It looks that way, as the parent normally will not executed further after the second batch exits.
But using a call before starting the second.bat, will show that the first batch isn't terminated.
parent.bat
echo parent.bat
call :myLabel
echo back in parent.bat main
exit /b
:myLabel
second.bat & echo back in parent.bat
exit /b
second.bat
echo second.bat
exit /b
I use here the the secpond.bat & echo back ... to avoid another bug/feature of cmd.exe.
If you use second.bat without any extras it will start second.bat AND jump to the label :myLabel in second.bat!
Call is basically saying "go execute this other batch file, and then come back here and continue". It has been there since DOS 3.3 or so, and if it were removed now would break all backward-compatibility (which is why people are still using batch scripts). It can also be used to branch to :link locations.
For info on the use and syntax (for future reference for others), you can see this MS TechNet link
If you need new functionality, use CMD scripts or PowerShell scripts instead.