Basically I'm trying to read from a text file of servers, ping them continuously, and output a timestamp when each server has rebooted (find "Request timed out.")
for /f "delims=" %%a in (Servers_List.txt) do (
start cmd /k ping -t %%a | find "Request timed out."
) && (Echo %%a rebooted at %time%.)
It launches each server ping -t in a separate window until I add the | find piece, and then it only launches one at a time after each subsequent window is closed.
Does anyone know a way to run each CMD window simultaneously without needing to close each prior window?
Thank you!
Your design cannot work :-(
Your first problem is you are piping the output of your START command, but you want to pipe the output of PING. That can be fixed by escaping the pipe as ^|.
You have a similar problem with your conditional command concatenation - you would need ^&^& instead of &&1.
But the biggest problem is your ECHO will not execute until the pipe is closed, which is never! FIND will continue to look for additional timeout lines after the first one is detected. It will not end until after the pipe is closed, and your ECHO will not run until after FIND terminates.
Your best bet would be to write a VBScript, or JScript, or PowerShell script to continuously ping a server, and write out your message with timestamp when timeout is detected.
You could write a batch script to loop through your list of servers and start a new custom ping process for each one. Or you could put everything into a single VBScript (or JScript, or Powershell) script.
Perhaps one of the following links can help get you started:
http://windowsitpro.com/scripting/how-can-i-use-vbscript-script-ping-machine
http://www.sems.org/2013/07/little-vbscript-to-continously-ping-a-host-with-timestamplog/
https://thwack.solarwinds.com/docs/DOC-135033
I am assuming you ideally would like to have all output in a single screen. You can use the START /B option to run multiple processes within the same console window, but then you run the risk of garbled output if two processes attempt to write to the console at the same time. This can be solved via the exclusive lock that is obtained when a process opens a file for writing. See How do you have shared log files under Windows? for some pointers on how this can help. That link is for shared log files, but it is easily adapted for shared console access.
I have used my JREPL.BAT utility to hack up a solution. Architecturally this is a convoluted abomination, but it works! It runs an endless PING process for each server, all in parallel within the same console. Each process pipes its output to JREPL which detects if the server responded and writes out a time-stamped message to both the console and a log file. The log file is also used as a lock file to coordinated shared access between the processes. Each process keeps track of the last server status, and a message is only written when the status changes.
#if (#X)==(#Y) #end /* Harmless hybrid line that begins a JScript comment
::***** Batch code *******
#echo off
setlocal enableDelayedExpansion
set "first="
copy nul serverStatus.log >nul
for /f "delims=" %%A in (Servers_List.txt) do if not defined first (
set "first=%%A"
) else (
start /b cmd /c ping -t %%A ^| jrepl "^Request timed out|^Reply from " "log('%%A','DOWN')|log('%%A','UP')" /t "|" /jmatch /jlib "%~f0"
)
if defined first ping -t %first% | jrepl "^Request timed out|^Reply from " "log('%first%','DOWN')|log('%first%','UP')" /t "|" /jmatch /a /jlib "%~f0"
exit /b
****** JScript code ******/
var status = 'initial';
var fso = new ActiveXObject("Scripting.FileSystemObject");
var file;
function log( server, newStatus ) {
if (status!=newStatus) {
status=newStatus;
var msg=new Date().toString()+' '+server+' is '+newStatus;
while (!openFile());
file.WriteLine(msg);
output.WriteLine(msg);
file.Close();
}
return false;
}
function openFile() {
try {
file = fso.OpenTextFile('serverStatus.log',8);
return true;
} catch(e) {
return false;
}
}
But, I don't see why it is necessary to run the processes in parallel. It seems to me you can simply enter an endless loop that cycles through the servers, and PINGs each server individually, writing out the status. Here is a simple batch script that does just that. Again, it only prints out a message when it detects a change in status.
#echo off
setlocal enableDelayedExpansion
for /l %%N in () do for /f "delims=" %%S in (Servers_List.txt) do (
ping /n 1 %%S | findstr /bc:"Request timed out" >nul && set "status='DOWN'" || SET "status=UP"
if "!status!" neq "!%%S!" (
set "%%S=!status!"
echo !date! !time! %%S is !status!
)
)
Related
There are things that we prefer not to understand in order to have an easier life to live.
But this is not something I can choose...
I made a batch file (or macro.doskey) to get the charset code. And it worked perfectly for a long time...
Basically it runs chcp:
> chcp
Code page active: 850
and then wraps the return before and after the colon
assigning what comes after to a variable:
FOR /F "tokens=1,* delims=:" %%s in ('CHCP') do (
#ECHO %%t
IF NOT "%1" == "" (SET %1=%%t)
)
For example:
> getCHCP.bat myVar
850
> ECHO %myVar%
850
However it started to lock, waiting for ENTER or displaying several echo messages. For example:
> getchcp myVar
ECHO is off.
ECHO is off.
ECHO is off.
ECHO is off.
ECHO is off.
ECHO is off.
ECHO is off.
ECHO is off.
850
I started to mix until I decided to change the ECHO %%t to ECHO %%s, and guess what?
No, is that the Bill Gates skull? Is it an easter egg from Microsoft? A virus?
No, none of that, this is just my autorun's welcome message.
This can be configured in
<[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]autorun>
In my case I called a batch file which, among other things, gives several echos showing this skull on the screen.
But the question is, why does it act like it reloads the autorun in background
when I've already opened the command prompt?
And why does it leave
everything in the buffer so that %%s pulls it again to the (Page code active) ':'?
And why are
you giving lots of ECHO is off on %%t when the only thing after
Code page active: is a number?
And the most important: How I solve it?
It's obvious, you already point to the problem.
this is just my autorun's welcome message.
This can be configured in
<[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor]autorun>
The line FOR /F %%s in ('CHCP') ... start CHCP but that will be done in a NEW child cmd.exe instance.
And a NEW cmd.exe instance runs the autorun command!
Just before it starts your chcp.
You can disable the autorun at all, or add some code to detect the difference between a new cmd.exe instance for the user against a new instance from a FOR /F.
Put this code at the start of your autorun batch file
#echo off
setlocal EnableDelayedExpansion
REM *** ALWAYS make a copy of the complete CMDCMDLINE, else you destroy the originial!!!
set "_ccl_=!cmdcmdline!"
REM *** %1 contains only data, when the script itself was called from the command line
if "%~1" NEQ "" (
goto :direct_call
)
REM *** The check is necessary to distinguish between a new cmd.exe instance for a user or for a "FOR /F" sub-command
if "!_ccl_:~1,-2!" == "!comspec!" (
REM ***** INTERACTIVE ****
REM *** Show your skull or something else
)
exit /b
I'm a computational biologist and I'm trying to run large batches of similar code with a single command, but my implementation has hit a brick wall.
I'm using the NEURON simulation environment, which uses MinGW for its Windows interface, which is where my research has shown my problem arises.
Currently, I am using a batch file to run all of these similar pieces of code, to iterate across the "collection" subfolders:
#echo off
for /D %%a in ("%cd%\all_cells\cell_*.*") do cd "%%a\sim1\" & START neuron sim.hoc
The problem arises when I have more than 32 subfolders; the additional instances won't run and will error with a "console device allocation failure: too many consoles" error.
My research has shown me that this is a known problem with Cygwin/MinGW.
However, working around this manually (ensuring that there is no more than 32 "collection" folders) is extremely time consuming when I am now dealing with hundreds of instances (each refers to a simulated cell and I want to gather statistics on hundreds of them), so I am trying to find a solution.
That said, I am terrible at writing batch files (I'm a terrible programmer who is used to scientific languages) and I can't figure out how to code around this.
It would be great if someone could help me either find a way around the 32 limit, or failing that, help me write a batch file that would do this:
-iterate over up to 32 folders
-wait for the instances to finish
-do it again for the next 32, until I reach the end of the folder.
I have tried using the /wait command to do them one at a time, but it still opens all 32. (And this wouldn't be ideal as I'd like to use all 16 cores I have.
The following is adapted from https://stackoverflow.com/a/11715437/1012053, which shows how to run any number of processes while limiting the total number run simultaneously in parallel. See that post for some explanation, though the code below is fairly well documented with comments.
I've eliminated the /O option and the code to work with PSEXEC from the original script.
The script below runs everything in one window - the output of each process is captured to a temporary lock file, and when finished, the full output of each process is typed to the screen, without any interleaving of process output. The start and end times of each process are also displayed. Of course you can redirect the output of the master script if you want to capture everything to a single file.
I've limited the total number of parallel processes to 16 - of course you can easily modify that limit.
The code will not work as written if any of your folder paths include the ! character. This could be fixed with a bit of extra code.
Other than that, the code should work, provided I haven't made any silly mistakes. I did not test this script, although the script it was derived from has been thoroughly tested.
#echo off
setlocal enableDelayedExpansion
:: Define the maximum number of parallel processes to run.
set "maxProc=16"
:: Get a unique base lock name for this particular instantiation.
:: Incorporate a timestamp from WMIC if possible, but don't fail if
:: WMIC not available. Also incorporate a random number.
set "lock="
for /f "skip=1 delims=-+ " %%T in ('2^>nul wmic os get localdatetime') do (
set "lock=%%T"
goto :break
)
:break
set "lock=%temp%\lock%lock%_%random%_"
:: Initialize the counters
set /a "startCount=0, endCount=0"
:: Clear any existing end flags
for /l %%N in (1 1 %maxProc%) do set "endProc%%N="
:: Launch the commands in a loop
set launch=1
for /D %%A in ("%cd%\all_cells\cell_*.*") do (
if !startCount! lss %maxProc% (
set /a "startCount+=1, nextProc=startCount"
) else (
call :wait
)
set "cmd!nextProc!=%%A"
echo -------------------------------------------------------------------------------
echo !time! - proc!nextProc!: starting %%A
2>nul del %lock%!nextProc!
cd "%%A\sim1\"
%= Redirect the output to the lock file and execute the command. The CMD process =%
%= will maintain an exclusive lock on the lock file until the process ends. =%
start /b "" cmd /c 1^>"%lock%!nextProc!" 2^>^&1 neuron sim.hoc
)
set "launch="
:wait
:: Wait for procs to finish in a loop
:: If still launching then return as soon as a proc ends
:: else wait for all procs to finish
:: redirect stderr to null to suppress any error message if redirection
:: within the loop fails.
for /l %%N in (1 1 %startCount%) do 2>nul (
%= Redirect an unused file handle to the lock file. If the process is =%
%= still running then redirection will fail and the IF body will not run =%
if not defined endProc%%N if exist "%lock%%%N" 9>>"%lock%%%N" (
%= Made it inside the IF body so the process must have finished =%
echo ===============================================================================
echo !time! - proc%%N: finished !cmd%%N!
type "%lock%%%N"
if defined launch (
set nextProc=%%N
exit /b
)
set /a "endCount+=1, endProc%%N=1"
)
)
if %endCount% lss %startCount% (
timeout 1 /nobreak >nul
goto :wait
)
2>nul del %lock%*
echo ===============================================================================
echo Thats all folks^^!
You could install screen or tmux in cygwin.
Then you can start all neuron instances in a screen/tmux session.
They will not open a new window, so there is no limit anymore.
I'm running a command in a batch file[Just for info: this command runs a python script which generates different outputs]
The code is as follows:
:meshfunc
echo "starting command"
echo "!inst_dir!runME.bat -parameter1 !parameter2! !parameter1v! -script pythonscript.py"
echo start_time=!time!
for /F "delims=" %%i IN ('"!inst_dir!runME.bat -parameter1 !parameter2! !parameter1v! -script pythonscript.py"') DO (
set cmdline=%%i
echo currentENDline=!cmdline!
)
EXIT /B 0
The output of the script is:
PS: I have stripped the output lines for obvious reasons, but they shouldn't matter here
So my questions is :
If i run the same command without a for loop , it will complete in maybe 30 sec without the warning at the end.
However, if I run it inside a for loop, it takes much longer about 3 mins and gives the warning at the end
Why am I'm getting the Warning at the end as shown. Why is this happening?
Because for /F execute the command enclosed in parentheses and store all its output in a temporary disk file until the command ends; after that, it start to repeatedly execute the group of commands. In this way, if the output is very large, the temporary file needs to grow several times and such a process takes some time... It should be faster to redirect the output to a disk file and then process such a file with for /F:
:meshfunc
echo "starting command"
echo "!inst_dir!runME.bat -parameter1 !parameter2! !parameter1v! -script pythonscript.py"
echo start_time=!time!
call "!inst_dir!runME.bat" -parameter1 !parameter2! !parameter1v! -script pythonscript.py > output.txt
for /F "delims=" %%i IN (output.txt) DO (
set cmdline=%%i
echo currentENDline=!cmdline!
)
EXIT /B 0
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%*"
I want to perform the following operations.
Read 1 word at a time from an input file consisting of many words.
Pass this word as an argument to another command line based application.
Run that application for a fixed amount of time, say 10 seconds.
Abort the execution of the application if it is still running after 10 seconds and go back, pick the next word from the input file and repeat steps 1 to 3.
Here is what I have written though it does not achieve exactly what I want it to:
#echo off
for /f %%i in ('type input.txt') do call:Routine %%i
:Routine
set app="myApp.exe"
set limit=60
%app% %1
goto Delay
:Delay
ping localhost -n %limit% > nul
The above script will introduce the delay after the execution of myApp has completed. However, I want it to run myApp.exe for not more than 10 seconds, if it does, then abort the application using taskkill and move on to the next word from the input file.
I searched for a solution online and came across this:
http://www.tech-archive.net/Archive/WinXP/microsoft.public.windowsxp.general/2006-04/msg03609.html
Though it does not answer my query exactly, I would like to make my code do something similar.
Thanks.
The logic in the linked code looks flawed: It either launches 3 download commands, or it delays ~59 seconds and attempts to kill all download commands, but it never does both. The TASKKILL command arguments are not correct - the imagename belongs after the /IM parameter.
In your code, you are not going to kill your task without the TASKKILL command!
You must GOTO :EOF or EXIT /B after your loop finishes, otherwise the code will fall through and execute the subroutine without using CALL. But there really is no need to use a subroutine at all.
You only need to initialize your variables once.
No need to execute a command in your IN() clause. FOR /F has a variation that can read the text file directly. Type HELP FOR from the command line and read the documentation carefully.
PING has roughly a 1 second delay between each echo request. So a count of 11 will yield a delay of roughly 10 seconds.
EDIT - originally forgot the critical START command to start the app in its own process
#echo off
set app="myApp.exe"
set limit=11
for /f %%i in (input.txt) do (
start "" %app% %%i
ping localhost -n %limit% > nul
taskkill /im %app% /f
)