Second Timeout function didn`t work in Batch File - windows

Working on a startup File (like a shortcut with special functions) for a game. (Counter-Strike Global Offensive)
The intention is to replace the menu music - some updates ago this was easier but right now I need to forging on a Batch file.
This is how it looks so far
#Echo off
"C:\Users\Cedo\Desktop\csgo\csgo\Counter-Strike Global Offensive.url"
Timeout 9
"C:\Users\Cedo\Desktop\csgo\csgo\csgomusic.m3u8"
[Timeout 240]!
[Taskkill /IM winamp.exe]! ----> Dont work!
Exit
and the way to here is more complicated than I thought. I solved already a lot of problems because a lot of the stuff on the internet seems outdated I guess.
The actual problem is a second timeout and taskkill for the winamp music file.
Strangely when inserting a second timeout (like the first) nothing happens.
Just after closing winamp manually the Timer starts!
But something needs to be done so that the second timeout with 240 secs and the taskkill to close winamp automatically works.
Another cool thing would be to start the winamp music file a bit later without minimizing the started game window.
(Already tried start /min commands - it doesn't matter actually - wont work if minimized or maximized)
Does someone know more? Right now I'm good with 9 Seconds(winamp opens before the game maximizes because this takes some time and so i didn`t get tabbed out).
And is there maybe a better way to build this up or improve things?

Just after closing winamp manually the Timer starts!
Given this statement I infer that "C:\Users\Cedo\Desktop\csgo\csgo\csgomusic.m3u8" is starting Winamp.
Note place the following script into the "C:\Users\Cedo\Desktop\csgo\csgo\" folder, or update the "_RootFolder" to have the correct path.
So essentially what you need to do is this:
Start a separate command window to run the task to kill winamp, because otherwise you will only move on tot the next command when Winamp is killed manually.
#(SETLOCAL
REM SET "KillCmd="%~f0" :Kill_Winamp"
REM SET "_StartKillCMD=start "Kill Winamp!" cmd /c %KillCmd%"
SET "_StartKillCMD=start "Kill Winamp!" cmd /c "%~f0" :Kill_Winamp"
SETLOCAL EnableDelayedExpansion
ECHO OFF
SET "eLvl=0"
REM SET "_RootFolder=%USERPROFILE%\Desktop\csgo\csgo\"
SET "_RootFolder=%~dp0"
SET "CSGo_URL=!_RootFolder!Counter-Strike Global Offensive.url"
SET "Music_File=!_RootFolder!csgomusic.m3u8"
SET /A "_Timer_WinAmp_Start=9", "_Timer_WinAmp_Kill= _Timer_WinAmp_Start + 240"
)
ECHO=%* | FIND /I ":Kill_Winamp" && (
CALL :Kill_Winamp
) || (
CALL :Main
)
( ENDLOCAL
Exit /b %_eLvl%
}
:Main
COLOR 2F
ECHO=======================================================================
ECHO= Launching CSGO, Waiting %_Timer_WinAmp_Start% seconds before starting Winamp.
ECHO=======================================================================
ECHO=
"%CSGo_URL%"
ECHO=
SETLOCAL DisableDelayedExpansion
%_StartKillCMD%
ECHO=
ECHO=Started Kill Timer..
ECHO=
Timeout %_Timer_WinAmp_Start%
"%Music_File%"
ECHO=Exiting, Remove the pause on the next line if not needed.
Pause
GOTO :EOF
:Kill_Winamp
COLOR 4F
ECHO=======================================================================
ECHO= Waiting %_Timer_WinAmp_Kill% Seconds before Killing Winamp.
ECHO=======================================================================
ECHO=
ECHO=
Timeout %_Timer_WinAmp_Kill%
Taskkill /IM winamp.exe
ECHO=Exiting, Remove the pause on the next line if not needed.
Pause
GOTO :EOF

Related

Waiting for Process to terminate on a remote PC using for loop

Should be able to sort this but I'm going round in circles. I know this has to do with setlocal
EnableDelayedExpansion, but I'm missing something.
Goal:
Execute a windows (cleanmgr.exe) script on a remote machine, wait till Cleanmgr.exe closes then have
the initiating script "type" the resultant log file (generated via cleanup script) from the remote
system in the CMD window.
What's working:
The script running on the remote machine runs fine, it echo's C: free drive space into a log file,
then cleans up the PC, and then re runs the disk space report and echo's result into same log file,
so the user can see (/have transparency of) the reclaimed space via the before & after results.
What's Broken:
The WMIC command to check for Cleanmgr.exe on the target PC, only works once, when it waits to retry
the variable containing the Hostname has been wiped out. I can see the behavior by echoing the
variable back.
Fix Attempts:
I have a hunch this has to do with the variable being lost once the if statement is ran within the
Parentheses. I have tried lots of options but they all behave the same. I have tried jumping the
process out to loop outside the original code using %1 instead of %%i but just cant quite get there.
Thanks for any improvements.
#echo off
pushd %~dp0
color 1e
setlocal EnableDelayedExpansion
title HDD Space Checker...
for /f %%i in (hostnames.txt) do (
xcopy /y cleanupwindows-sfd.bat \\%%i\C$\IT
WMIC /node:"%%i" process call create "C:\IT\cleanupwindows-sfd.bat"
echo Waiting For Processes...
timeout -t 10 /nobreak >nul
:loop
WMIC /node:"%%i" process where name="cleanmgr.exe" get name |find "cleanmgr.exe">nul
IF "!errorlevel!"=="0" set running=var
IF "!running!"=="var" timeout -t 4 >nul & echo Still Running & goto :loop
IF "!running!"=="" timeout -t 4 >nul & type \\%%i\C$\IT\%%i_HHD_Space.log
)
pause
exit
There is at least two points to see.
Your running variable, once set, is never reset, triggering an infinite loop
Your goto statement inside the enclosing parenthesis drives the command interpreter (cmd.exe) to stop evaluating the block, thus your script loose the %%i and leave the for loop, thus when terminating the :loop cycle your script will leave the for loop without cycling to other values in hostnames.txt.
To address that, put your process code in a subroutine called with CALL and reset the running variable at each :loop cycle :
#echo off
pushd %~dp0
color 1e
setlocal EnableDelayedExpansion
title HDD Space Checker...
for /f %%i in (hostnames.txt) do (
CALL:Process "%%i"
)
pause
exit
:Process
xcopy /y cleanupwindows-sfd.bat \\%~1\C$\IT
WMIC /node:"%~1" process call create "C:\IT\cleanupwindows-sfd.bat"
echo Waiting For Processes...
timeout -t 10 /nobreak >nul
:loop
set "running="
WMIC /node:"%~1" process where name="cleanmgr.exe" get name |find "cleanmgr.exe">nul
IF "!errorlevel!"=="0" set "running=var"
IF "!running!"=="var" timeout -t 4 >nul & echo Still Running & goto :loop
IF "!running!"=="" timeout -t 4 >nul & type \\%~1\C$\IT\%~1_HHD_Space.log
GOTO:EOF
Explanations: The CALL statement implies that the command interpreter will store the current executed line of your script and its state before executing the associated subprocess/command/etc.. When the subprocess/command/etc.. finishes, the command interpreter resumes its execution of the script to the next line with a restored context. This avoids then the loose of the for loop context.

Using "IF Loop" to open PDF if closed

I am trying to make a batch file that will continuously check if a certain PDF is open, if not than it opens said PDF file.
I am new to batch file programming but have been able to create a batch file with "start" that opens the specific PDF. After doing some research I believe a for loop with use of a task-list might be able to get me what I need but do not really know how to implement it.
start AcroRd32.exe "C:\Users\user1\Documents\Folder1\PDF Forms\App.pdf "
Any help would be appreciated!
UNTESTED
This might work for you. I think it will fail if the pdf is read only to the user.
WARNING Be very careful typing or copying type nul>>"%filename%". If you mess it up you can erase the contents of the file. It must be double >>. A single > will erase the contents.
set "fname=C:\Users\user1\Documents\Folder1\PDF Forms\App.pdf"
:infiniteloop
:: Check if the file is accessible, goto openfile if it is
(type nul>>"%fname%" && goto :openfile) >nul 2>nul
:: 1 second delay so the loop doesn't go crazy
timeout /t 1 >nul
goto :infiniteloop
:openfile
start AcroRd32.exe "%fname%"
:: Give it time to open before checking again
:: I chose 30 seconds
timeout /t 30>nul
goto :infiniteloop
This depends on the file being "locked" or whatever by the program that has it open. On my computer, Adobe Reader does "lock" a PDF file, but Microsoft Edge does not.
You do not need a for loop for this it would just create an extra process which is not needed.
#echo off
:start
tasklist /FI "WINDOWTITLE eq App.pdf" | find /i "AcroRd32.exe"
if errorlevel 1 start AcroRd32.exe "C:\Users\user1\Documents\Folder1\PDF Forms\App.pdf"
timeout /t 10 >nul 2>&1
goto :start

Limit number of batch files launched by FOR loop

I have a batch file that downloads 21 separate files (all very large). I have timeouts to try to allow some files to finish before trying to get more (not a nice solution), and if they all run together it uses all my system resources and everything grinds to a halt.
I can put /WAIT in my FOR loop, then it will only launch one download task at a time. I would like to launch 5 download windows, and once they are finished, continue with the next 5. I've tried to use GOTO to wait until finished but it does not work.
rem *****Step 1*****
set tstart=0
set tend=5
set incr=1
FOR /L %%a IN (%tstart%,%incr%,%tend%) DO (start /ABOVENORMAL GET_PIECE_ENSEMBLE.bat %newformatdate% 0%%a %HH%)
timeout /t 30
:WAIT
if exist flagfile.* goto WAIT
Then I have 4 more steps to download files 5-10, 11-16, etc. But if it takes extra long to download, the script moves along and starts downloading more files or moves on to the next processes, which require all the data to be downloaded first. I am trying 2 things here: a short timeout to ensure the data has started downloading (so a flagfile exists), and a wait if flagfile exists. But the wait isn't working, the script still moves along and starts downloading the next bunch. My workaround has been to set a timeout of 600 but sometimes the data takes more than 10 min to download, sometimes it hangs and takes up to two hours, sometimes only a few minutes. If all the pieces launch at once, it freezes up the workstation and we have to restart the machine.
And here is the code of GET_PIECE_ENSEMBLE, the script that runs in the FOR loop:
set "newformatdate=%1"
set "ename=%2"
set "HH=%3"
echo %time% > flagfile.%ename%
wget -O C:/Data/DataFile_%HH%.%ename%.grb2 http://url/%newformatdate%.grib2
del flagfile.%ename%
Looking for advice on either how to make the "wait if flagfile exists" work (any idea why this isn't working?), or if there is some simple argument I can put in my FOR loop to limit the number of instances so downloads finish before others start? Surely there is a better solution than guessing at a timeout interval when the network conditions vary so much!
In GET_PIECE_ENSEMBLE
try
...
set "HH=%3"
:only5
set /a running=0
for /f %%c in ('dir /b/a-d flagfile.* 2^>nul') do set /a running+=1
if %running% geq 5 (
timeout /t 1 >nul
goto only5
)
echo %time% > flagfile.%ename%
...
which should count the flagfiles and while there are 5 or more, delay 1 sec.
As soon as one flagfile is deleted, the next queued file to get activated will bump the number of in-process files to 5 again, blocking the other queued processes.
UNTESTED!!!
:: Start with list of files to download as parameters
:: down.cmd "file1" "file2" "file3" etc..
#echo off
set "url=http://host.com"
:loop
for /f %%a in ('tasklist /FI "imagename eq wget.exe" /FI "windowtitle eq _download*" /FO csv /NH ^| find /C "wget"') do (
if %%a LEQ 5 (
start "_download" "%path%\wget.exe" -O "%~1" "%url%/%~1"
shift
)
timeout /T 5
if not "%~1"=="" goto loop

Batch File to Loop a VBScript During Certain Times of the Day

I am very new to batch, so please be easy. That being said, I am attempting to write a batch file that every few minutes will execute a VBScript that updates information on our network. Currently I have:
#ECHO OFF
REM Run VBScript
GOTO SKIP01
:LOOP
wscript "C:\Users\Desktop\RenameMove.vbs"
GOTO SKIP01
:
:
:SKIP01
REM 3 Min Delay
PING 1.1.1.1 -n 10 -w 18000 >NUL
IF HOUR:%time:~0,5% <09:44
GOTO SKIP01
IF HOUR:%time:~0,5% >16:00
GOTO SKIP01
IF HOUR:%time:~0,5% >=09:45
GOTO LOOP
Currently the window will open for a minute or two, then it just closes. If I run the above after removing the first GOTO SKIP01, the VBScript executes perfectly. Then there is a delay and the window closes. I imagine that it is everything under :SKIP01 that is causing the problem. The function I am trying to achieve is for the .BAT file to continuously loop a delay between the hours of 16:01 - 09:44. Then run the VBScript every 3 minutes from 09:45 - 16:00. Searching the forums I have yet to find anything to help. As I said, I am very new to Batch.
RE: My last comment below:
#ECHO OFF
setlocal enableextensions disabledelayedexpansion
set delay=180
set "startTime=09:45"
set "endTime=16:00"
:LOOP
set "now=%time: =0%"
:: ECHO It's %now%, waiting %delay% seconds
echo %date% %time% >> log.txt
if "%now%" geq "%startTime%:00,00" (
GOTO LOOP2
)
:LOOP2
set "now=%time: =0%"
:: ECHO It's %now%, waiting %delay% seconds
echo %date% %time% >> log.txt
if "%now%" lss "%endTime%:00,00" (
:: ECHO Start MSAccess
GOTO CALLVBS
)
:WAIT
REM 10 second delay
PING 127.0.0.1 -n %delay% >REM
GOTO LOOP
:CALLVBS
echo Time to Move!
wscript "C:\Users\ljs\Desktop\Stock_Automation_DO_NOT_EDIT\RenameMove.vbs"
GOTO WAIT
I renamed some things so it made more sense to me, but here is a working version. there are variables at the top for the delay (in seconds) and the start and end time for the window. I also modified your ping delay to something more simple, in my opinion.
Remove the echos for production and add the vbscript file to the CALLVBS function.
#ECHO OFF
setlocal enableextensions disabledelayedexpansion
set delay=180
set "startTime=09:45"
set "endTime=16:00"
:LOOP
set "now=%time: =0%"
ECHO It's %now%, waiting %delay% seconds
if "%now%" geq "%startTime%:00,00" (
ECHO It's after %startTime%
if "%now%" lss "%endTime%:00,00" (
ECHO And it's before %endTime%
GOTO CALLVBS
)
)
:WAIT
REM 10 second delay
PING 127.0.0.1 -n %delay% >REM
GOTO LOOP
:CALLVBS
echo It's time!
REM Call VBS here
GOTO WAIT
Here's what's happening, as explained by someone who barely knows what he's talking about:
ABOUT GOTO
First you need to know that batch files process line-by-line from top to bottom, unless it encounters certain statements like if, for or goto, the last of which being the one we are concerned with here. If the interpreter encounters a GOTO command, it will go to the corresponding label and resume processing code line by line until it finds another GOTO or gets to the end of the file.
SIMPLE EXAMPLE
#echo off
GOTO :FRUITS
:COLORS
echo Red
echo Green
GOTO :END
:FRUITS
echo Apple
echo Banana
GOTO :COLORS
:END
echo Done!
This outputs the following:
Apple
Banana
Red
Green
Done!
BREAKDOWN
Set up variables
#ECHO OFF
setlocal enableextensions disabledelayedexpansion
set delay=180
set "startTime=09:45"
set "endTime=16:00"
This sets some settings and creates some variables for use later. The variables should be self explanatory but
I can elaborate if you want.
check the time
01. :LOOP
02. set "now=%time: =0%"
03. ECHO It's %now%, waiting %delay% seconds
04. if "%now%" geq "%startTime%:00,00" (
05. ECHO It's after %startTime%
06. if "%now%" lss "%endTime%:00,00" (
07. ECHO And it's before %endTime%
08. GOTO CALLVBS
09. )
10. )
This is our "loop". :LOOP denotes what is basically a labeled
section of code that we can go back to any time we want. I called it LOOP
because it is the section we are doing over and over. It may have been more
accurate to call it :CHECKTIME or something similar, as that's what it does.
The label means absolutely nothing to the interpreter so calling it "LOOP"
doesn't mean it's going to repeat. This may be the biggest source of confusion.
Here is a
step-by-step of what each line on this block does (not processing
conditions, just line by line):
Get the current time.
Output the current time
Compare the current time to the endTime variable.
Output the result.
Compare the current time to the startTimevariable.
Output the result.
Goto the CALLVBS section of code
Note that I could have put GOTO WAIT at the end of this block and that might make more sense, but since :WAIT is the next block of
code that's what will process next anyway, so the GOTO would be
superfluous! This may be a second point of confusion.
Wait a bit
:WAIT
REM 10 second delay
PING 127.0.0.1 -n %delay% >REM
GOTO LOOP
This is the section of code that simply waits the specified number of
seconds. It does this using the ping command which is common for
batch programming since there is no built-in delay or sleep command
like other languages have. The first line is simply a comment, REM
means "Remove" (I think) and is how you comment out lines of code in
batch. As a matter of fact, I should have removed this since it's
not 10 seconds anyway :). The second line pings the localhost 180
times (or whatever the delay variable is set to). The >REM part
means it outputs the results of the ping to, well, I've never seen
"REM" here. Usually you would output it to nul but either way, it's
making sure you don't see the 180 ping results. Now, the 3rd line
tells the processor to go back to the :LOOP label. No matter what.
After the ping, it does the :LOOP section code again.
Execute your vbscript
:CALLVBS
echo It's time!
REM Call VBS here
GOTO WAIT
This is the :CALLVBS section of code. First it outputs "It's
time!". The second line is again, a comment. As you know, you
replace this with your vbscript. After this, the interpreter is told
to go to the :WAIT section of code. Again, it will always do this,
no matter what, after executing the line above it.

Possible to launch multiple threads of commands in cmd?

I have about 290 files that I need to optimize in a short period of time.
When I do optipng *.png it takes about 10 minutes to complete the transaction.
However when I do optipng a*.png and optipng m*.png in two separate command line it gets the work done in 5 minutes.
Now is there a way I can launch about 20 processes at the same time which will get the work done faster and not take up all of the space on my Desktop?
I have written a batch file that executes only a maximum number of commands a while ago: Parallel execution of shell processes:
#echo off
for /l %%i in (1,1,20) do call :loop %%i
goto :eof
:loop
call :checkinstances
if %INSTANCES% LSS 5 (
rem just a dummy program that waits instead of doing useful stuff
rem but suffices for now
echo Starting processing instance for %1
start /min wait.exe 5 sec
goto :eof
)
rem wait a second, can be adjusted with -w (-n 2 because the first ping returns immediately;
rem otherwise just use an address that's unused and -n 1)
echo Waiting for instances to close ...
ping -n 2 ::1 >nul 2>&1
rem jump back to see whether we can spawn a new process now
goto loop
goto :eof
:checkinstances
rem this could probably be done better. But INSTANCES should contain the number of running instances afterwards.
for /f "usebackq" %%t in (`tasklist /fo csv /fi "imagename eq wait.exe"^|wc -l`) do set INSTANCES=%%t
goto :eof
It spawns a maximum of four new processes that execute in parallel and minimized. Wait time needs to be adjusted probably, depending on how much each process does and how long it is running. You probably also need to adjust the process name for which tasklist is looking if you're doing something else.
There is no way to properly count the processes that are spawned by this batch, though. One way would be to create a random number at the start of the batch (%RANDOM%) and create a helper batch that does the processing (or spawns the processing program) but which can set its window title to a parameter:
#echo off
title %1
"%2" "%3"
This would be a simple batch that sets its title to the first parameter and then runs the second parameter with the third as argument. You can then filter in tasklist by selecting only processes with the specified window title (tasklist /fi "windowtitle eq ..."). This should work fairly reliable and prevents too many false positives. Searching for cmd.exe would be a bad idea if you still have some instances running, as that limits your pool of worker processes.
You can use %NUMBER_OF_PROCESSORS% to create a sensible default of how many instances to spawn.
You can also easily adapt this to use psexec to spawn the processes remotely (but wouldn't be very viable as you have to have admin privileges on the other machine as well as provide the password in the batch). You would have to use process names for filtering then, though.
Looks like you can write a batch file and run your commands asynchronously from that file.
Running Windows batch file commands asynchronously

Resources