if condition and for loop in batch file - batch-file

I need to check whether a file is created and locked or not, using a batch file,
if the file is locked the program should wait and check periodically whether the file is released from lock, and when it gets unlocked the program should exit.
I am very new to writing batch files (started today)
This is what I have tried:
#echo off
:loop
if (2<nul (>>test.txt echo off))(
goto END
)
else (goto MESSAGE)
:MESSAGE
echo trying to access file
goto loop
:END
pause

You were close :) But you cannot use IF to directly test whether a command succeeded or not. Use the || conditional operator instead.
Assuming you mean Windows, and not DOS:
#echo off
:loop
2>nul (
(call ) >>test.txt
) || (
echo Trying to access file
timeout /nobreak 1 >nul
goto loop
)
(call ) is simply a very efficient way to perform a no-op that always returns success.
The TIMEOUT introduces a 1 second delay to prevent the loop from hogging CPU resources.
See How to check in command-line if a given file or directory is locked (used by any process)? for more info on how the above works.

Related

Make a batch file give itself admin privilages and self destruct

I am coding a virus using some BSOD code (I didn't make that part of it) and I want the file to, one started, give itself admin privilages, set the date and time to a certain point, and once the system date and time hit a certain point, self destruct. The problem I'm coming accross is that, the file is stuck on the process of keeping the BSOD on the screen, so it never gets to the self destruct code. Any help would be greatly appreciated. Thanks.
I've tried looping it and putting the code to when the loop reaches a certain point, move on. Didn't work.
#echo off
set loopcount=1
:loop
(
echo ^<html^>^<head^>^<title^>Microsoft Windows^</title^>
echo.
echo ^<hta:application id="oBVC"
echo applicationname="BSOD"
echo version="1.0"
echo maximizebutton="no"
echo minimizebutton="no"
echo sysmenu="no"
echo Caption="no"
echo windowstate="maximize"/^>
(This is the beggining of the code. It should loop once, then self destruct itself)
start "" /wait "bsod.hta"
del /f /q "bsod.hta" > nul
set /a loopcount=loopcount-1
if %loopcount%==0 goto exitloop
goto loop
:exitloop
del "%~f0" & exit
(This is the part where it should have made itself self destruct after the loop happened)
I expected it to loop once, the self destruct, ending all processes. It just stayed on the process of holding up the BSOD
I expected it to loop once, the self destruct, ending all processes.
It just stayed on the process of holding up the BSOD
Your start /wait command will most certainly wait until the hta file has been closed prior to continuing execution of the rest of the batch file. If you wish to have the hta display and continue execution, just remove the /wait
start "bsod.hta"

Start command does not allow the script to fully close until secondary script closes

I've got a batch script start.bat that creates a locked .lock file (basically, to verify if the script is running, by attempting to delete it) and starts a bunch of secondary looping batch scripts (they keep on running after start.bat is closed). The problem is when start.bat is closed the locked file remains locked, until ALL of the secondary scripts are closed.
Question: Are there any alternative methods to run secondary batch scripts without locking up the primary script until secondary ones are finished?
I feel like most of this code is irrelevant, but included it in case somebody wants to test it.
#echo off
set "started="
<nul >"%~nx0.lock" set /p ".=." ::Rewrite lock file with a single dot
2>nul (
9>>"%~f0.lock" (
set "started=1"
call :start
)
)
#if defined started (
del "%~f0.lock">nul 2>nul
) else (
exit //script closes
)
exit /b
:start
//irrelevant loop logic
Start pause.bat //Pause command to keep pause.bat open
//starts other batch files too
Seems like the file may still be in use?
Try:
del /F "%~f0.lock">nul 2>nul
Your problem are inherited handles. When you start a process with a redirection active, the process inherits the redirection. So, you need to keep the lock but start the processes while not keeping it.
You can try some variation of this
#echo off
setlocal enableextensions disabledelayedexpansion
rem Check if this is a lock instance
if "%~1"==".LOCK." goto :eof
rem Retrieve all the needed data to handle locking
call :getCurrentFile f0
for %%a in ("%f0%") do set "lockFile=%%~nxa.lock"
set "lockID=%random%%random%%random%%random%%random%%random%%random%"
rem Try to adquire lock
set "started="
2>nul (
>"%lockFile%" (
rem We get the lock - start a hidden instance to maintain the lock
set "started=1"
start "" /b cmd /k""%f0%" .LOCK. %lockID%"
)
)
rem Check if the lock was sucessful
if not defined started (
echo lock failed
pause
goto :eof
)
rem Launch the child processes, now detached from lock, as this cmd instance
rem is not holding it. The hidden cmd /k instance holds the lock
start "" write.exe
start "" notepad.exe
start "" /wait winver.exe
rem Once done, release the locked instance
>nul 2>nul (
wmic process where "name='cmd.exe' and commandline like '%%.LOCK. %lockid%%%'" call terminate
)
rem And remove the lock file
del "%lockFile%"
rem Done
goto :eof
rem To prevent problems: http://stackoverflow.com/q/12141482/2861476
:getCurrentFile returnVar
set "%~1=%~f0"
goto :eof
As the hidden cmd instance is hosted inside the same console than the current batch file, if you close the console, the lock is released (but the lock file is not deleted)

Check if CTRL-C pressed Or batch wait for agreement

I need to know how can I check in batch file if the second batch file which is opened in other command window has stopped (waiting for argument or process not successful).
#echo off
:loop
start /wait rapidminer-batch.bat -f C:\Users\AHM-PC\Documents\ccc.rmp
echo cmd stopped
pause
goto loop
When called batch ends, it returns a value to errorlevel. It works for call, don't know if for start too.
if %errorlevel% gtr 0 (
echo failed
) else (
echo success)
or call exit /b <number of error> in your called batch, to return specific value. Check exit for more details.
The normal method to provide interbatch communication is a flag file
Either you create a flag file in the main routine and wait for the started routine to delete it or wait until the started batch creates a file, and delete it.
eg
echo.>myflag.txt
start anotherbatch.bat
:loop
timeout /t 1 >nul
if exist myflag.txt goto loop
Here, the batch will wait until myflag.txt is deleted, which you do in the second batch. All you need is for the two routines to agree on a filename to use.

How to make a batch program wait until it detects a file

So here is what I have
:a
IF EXIST D:\ (
goto copy
) ELSE (
goto wait
)
:wait
timeout /t 300
goto a
:copy
xcopy "D:\photos" "C:\Photos From Cam"
exit
but I'm not very fond of this becuase that means it is always looping, except for the 5 minute pause, and I would like a way to do this where it doesnt run any code until it detects the files. Is this possible?
I do not think there's a better solution using just shell, than the one you already have.
You would have to use some external tools
For example refer to question Windows: File Monitoring Script (Batch/VBS).

How do I redirect the ouput of a program when it is executed via the START command and order the output

I'd like to do something like:
start program1 ^>output
start program2 ^>output
and order the output so that the output is ordered. I don't care which output is first (program1's or program2's), but I'd like it to be a complete dump of that program's output.
Ideally, I'd like to run multiple programs in parallel with output all going to the screen, but a file would be fine. I don't need to see it while it is running, just have it intelligible when it's finished.
EDIT:
If I follow the suggestion to output them to a file then merge them when it's done, I have the trouble of waiting 'til all the programs are done -- so, I need a waitUntilAllTheStartsAreFinished command.
start program1 ^>output1
start program2 ^>output2
#... as many as I need
waitUntilAllTheStartsAreFinished
TYPE output1
TYPE output2
delete output1
delete output2
You can use a loop and tasklist:
:wait
rem just wait a second before looking again
ping -n 2 ::1 >nul 2>nul
tasklist 2>&1 | findstr /b "program1.exe program2.exe ..." >nul 2>&1 && goto wait
It will only continue further until all of program1.exe program2.exe ... are terminated.
Joey's suggested method will work, but it can become problematic if your programs can be launched multiple times. It becomes difficult to tell which tasks are the onces you want to monitor.
Each program will have an exclusive lock on the temporary output file until the program finishes. Any attempt by another process to redirect to the same file will fail. This can be used to detect when the program finishes.
I used TIMEOUT to insert a delay in the polling. If you are on a system like XP that does not have TIMEOUT then you can use ping -n 2 ::1 >nul 2>nul instead.
I've included extensive documentation on how this solution works in the code. Edit - I have simplified the code a bit by removing one unneccessary code block level, and I improved the documentation.
#echo off
setlocal
REM Define a base name for the temporary output files. I've incorporated
REM a random number in the file name to generally make it safe to run this
REM master script multiple times simultaneously. It is unlikely a collision
REM will occur, but incorporating a timestamp in the name would make it more
REM reliable.
set "baseName=%temp%\output%random%_"
set /a "progCount=2, completedCount=0"
REM Start each program with both stdout and stderr redirected to a temporary
REM ouptut file. The program will have an exclusive lock on the output file
REM until it finishes executing. I've assumed the program is another batch file
REM and I use the START /B switch so that the programs are run in the same
REM window as this master script. Any console program will work, and the
REM /B switch is optional.
start /b "" ^"cmd /c test.bat ^>"%baseName%1" 2^>^&1^"
start /b "" ^"cmd /c test2.bat ^>"%baseName%2" 2^>^&1^"
REM etc.
REM Clear any existing completed flags, just in case
for /l %%N in (1 1 %progCount%) do set "completed%%N="
:loopUntilDone
REM Introduce a delay so we don't inundate the CPU while we poll
timeout /nobreak 1 >nul
REM Loop through each of the output file numbers.
REM Redirect the stderr for the DO block to nul so that if the inner
REM block redirection fails, the error message will be suppressed.
for /l %%N in (1 1 %progCount%) do (
REM Only test this particular program if the output file has been
REM created (in other words, the program has started) and we haven't
REM already detected that it has finished. Also redirect an unused
REM file handle to the output file in append mode. The redirection will
REM fail if the program has not completed. If the redirection fails then
REM the IF block is not executed.
if not defined completed%%N if exist "%baseName%%%N" (
REM We are within the block, meaning the redirection succeeded and
REM the program must have finished. So print out the results.
echo(
echo Ouput for program%%N
echo ---------------------------------------------
type "%baseName%%%N"
REM Set a flag so we know this program has finished
set completed%%N=1
REM Increment the completed count so we know when we are done
set /a completedCount+=1
) 9>>"%baseName%%%N"
) 2>nul
if %completedCount% neq %progCount% goto :loopUntilDone
del "%baseName%*"

Resources