Is it possible to retrieve the data output when you type ping in the command line?
Currently, we have existing servers that we check daily by typing ping (IP address) in the command line but only during at the start of the day. We would only be notified if the server is down once the user notifies us. The manual thing to do is to do a continuous ping and do a timely check of whether it was disconnected or not.
I would like to create something that would give out a prompt (while doing a continuous ping in the background) once the command line declares that there's a "request time out", intermittent connection, etc. So that there wouldn't be a need to manually check the status of the connection.
I realize this was asked 2 months ago, but it was never answered. Hopefully this will still be useful to someone. Granted, it's much easier to implement using a "real" programming language, but sometimes you need something that uses only built-in commands.
Configure as needed via environment variables (examples shown). To be polite, you probably want to use much longer timeouts if you don't own the server you're pinging.
#echo off
cls
setlocal enabledelayedexpansion
REM Configuration:
set SERVER=google.com
set TIMEOUT_AFTER_PING_FAIL_SECONDS=5
set TIMEOUT_AFTER_PING_SUCCEED_SECONDS=10
set TIMEOUT_AFTER_LINK_DOWN_SECONDS=15
set DECLARE_LINK_DOWN_FAILS=5
set CONSECUTIVE_FAIL_COUNT=0
:Start
set PING_RESULT=Failure
for /f "delims=" %%X in ('ping /n 1 %SERVER%') do (
set TEMPVAR=%%X
if "Reply from"=="!TEMPVAR:~0,10!" set PING_RESULT=Success
)
goto:!PING_RESULT!
:Success
echo Ping Succeeded
set CONSECUTIVE_FAIL_COUNT=0
call:Sleep %TIMEOUT_AFTER_PING_SUCCEED_SECONDS%
goto:Start
:Failure
set /A CONSECUTIVE_FAIL_COUNT+=1
echo Ping Failed !CONSECUTIVE_FAIL_COUNT! Time(s)
if !CONSECUTIVE_FAIL_COUNT!==%DECLARE_LINK_DOWN_FAILS% (call:LinkDownHandler&goto:Start)
call:Sleep %TIMEOUT_AFTER_PING_FAIL_SECONDS%
goto:Start
:Sleep
REM See http://stackoverflow.com/questions/4317020/windows-batch-sleep
setlocal
set /A ITERATIONS=%1+1
ping -n %ITERATIONS% 127.0.0.1 >nul
goto:eof
:LinkDownHandler
echo Link is Down
set CONSECUTIVE_FAIL_COUNT=0
REM Add additional link-down handler actions here
call:Sleep %TIMEOUT_AFTER_LINK_DOWN_SECONDS%
goto:eof
Related
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.
First of all I'm a complete noob to Batch scripting and networking, with that being said, Here is what I'm trying to accomplish.
I want to check if the server has provided this batch file executing pc to access it, if not create access to it. (many different pc's run this batch file)
This what I came up with for now in my batch file.
net use * \\ip\My_WebApp /Persistent:yes /user:Username Password
Exit
This batch file command create access to the server just fine but It creates a new access connection every time this file get executed. Which is not needed and might crash the server load.
How can I check if the Server already has provided the access, Only if not, execute the above command in a batch file. my logic like like....
boolean status = check_server_accessibility()
if(!status){
net use * \\ip\My_WebApp /Persistent:yes /user:Username Password
}
Exit
Appreciate any help, Thank you so much for your time.
You should only ever need to perform the task once, which suggests that a scripted solution isn't needed, (persistent means that!). I would assume that the following may be sufficient:
#Set "MyMap=\\ip\My_WebApp"
#%__AppDir__%wbem\WMIC.exe LogicalDisk Where "DriveType='4'" Get ProviderPath 2>NUL | %__AppDir__%findstr.exe /R /I "%MyMap:\=\\%\>" 1>NUL && GoTo :EOF
#%__AppDir__%net.exe Use * "%MyMap%" /Persistent:Yes /User:Username Password
Alternatively, if you needed to know which drive letter is currently assigned to it, then use a for-loop to retrieve the data:
#Echo Off
SetLocal EnableExtensions DisableDelayedExpansion
Call :MapChk "\\ip\My_WebApp"
GoTo :EOF
:MapChk
Set "MyMap=%~1"
Set "MyDrv="
For /F "Skip=1 Delims=:" %%G In (
'%__AppDir__%wbem\WMIC.exe LogicalDisk Where "DriveType='4' And ProviderPath='%MyMap:\=\\%'" Get DeviceID 2^>NUL'
) Do For %%H In (%%G) Do Set "MyDrv=%%G:"
If Defined MyDrv (
Echo %MyMap% is already mapped to drive %MyDrv%.
%__AppDir__%timeout.exe /T 5 /NoBreak >NUL
GoTo :EOF
)
%__AppDir__%net.exe Use * "%MyMap%" /Persistent:Yes /User:Username Password
I've made this one into a callable label, so that you can more easily extend it for other mappings too, e.g. before the GoTo :EOF, (line 6), Call :MapChk "\\Server\My Share".
Note: These solutions are untested, I do not use a PC and have no mapped network locations to test them against. Please let me know if I have made a mistake somewhere.
Hi so my coworker requested if I could make a script. I am sure what I want it to do and wrote some pseudo code in bash style now of course this not useable for him since he is on Windows. So I tried to implement in a .bat script now here is where my knowledge comes a bit short. What I need the script to do is to connect to a certain VPN-ip if that is not avaible the localsystem should foward it to another VPN so he doesn't need to worry about it. Either one of 2 should always be reachable. But they are never at the same time. This is for test tooling.
Pseudo bashcode
while true
do
From local if
10.10.1.15 avaible connect to it
else
10.168.84.47 connect to it
elseif
try to connect to 10.10.1.15 again && verify that'
else
echo 'error device over VPN unavaible'
My Attempted batch script, I am pretty sure that what I have now is not gonna work
#setlocal enableextensions enabledelayedexpansion
#echo off
set ipaddr=%1
:loop
set state=down
for /f "tokens=5,6,7" %%a in ('ping -n 1 !ipaddr!') do (
if "x%%b"=="xunreachable." goto :endloop
if "x%%a"=="xReceived" if "x%%c"=="x1," set state=up
)
:endloop
echo.Link is !state!
ping -n 6 10.10.1.15 >nul: 2>nul:
goto :loop
endlocal
IF EXIST 10.10.1.15 (
is reacheable connect
) ELSE (
netsh interface portproxy add v4tov4 listenport=80 connectaddress=10.10.1.15 fowardaddress=10.168.84.47
)
So far as I know, you can just try to find "ttl=" to determine success/fail for a ping without worrying about all those tokens and different versions of cmd. I'm sure someone will correct me if I'm wrong.
untested
set ip=127.0.0.1
rem 2 pings waiting 900ms for a reply
ping -n 2 -w 900 %ip%|find /i "ttl=">nul || goto :fail
rem If we get here then the ping succeeded
rem do what you want here
goto :eof
:fail
rem If we get here then the ping failed.
rem do what you want here
goto :eof
The || operator basically means "if the previous command fails then do this".
Ok basic premise here is that there are two servers with the same files.
I'm using Windows command line scripts and opening a network share to either of those hosts. I'd like to make the script "smart" so it works out the latency between the two servers and picks the lowest latency host to connect to.
I want to do something like (i know this code doesn't work, it's an example of my concept):
PING 192.168.0.1
SET HOST1=%ERRORLEVEL%
PING 192.168.0.2
SET HOST2=%ERRORLEVEL%
IF HOST1 GTR HOST2 GOTO HOST2CONNECT
:HOST1CONNECT
NET USE X: \\HOST1 (etc)
:HOST2CONNECT
NET USE X: \\HOST2 (etc)
Does that make any sense? I just can't seem to visualise a way of comparing the data from the two PING tests and i know that ERRORLEVEL in this situation is just a 1 or 0 based upon whether the ping is successful or not, so it's garbage as they'll both succeed. But i'm hoping you get the concept and can see what i'm missing.
Thanks in advance.
#echo off
setlocal enableextensions disabledelayedexpansion
rem Initialize variables
set "selected="
set "min=99999999"
set serverList= "bing.com" "duckduckgo.com" "google.es" "google.com"
echo - Testing -----------------------------
rem Enumerate the hosts to check
for %%a in ( %serverList% ) do (
rem Ping the host and retrieve the average roundtrip
for /f "tokens=6 delims== " %%r in ('
ping -n 1 "%%~a" ^| findstr /r /c:"^ .*ms$"
') do for /f "delims=ms" %%t in ("%%r") do (
echo "%%~a" : %%t ms
rem Determine if the current host has a lower rtt
rem if %%t geq min or min is already 0, then we have
rem a division by 0, else a lower rtt has been found
set /a "1/(min/(%%t+1))" && (
set "selected=%%~a"
set "min=%%t"
)
rem Of course this can be done with delayed expansion,
rem just a question of personal preferences
)
) 2>nul
echo(
echo - Selected ----------------------------
echo %selected% : %min%
MC ND posted his answer before I did so he should deserve the credits. Please do not mark this as the answer, that is not the intention of my post.
Because I had already made the effort of posting an answer as well and for sake of completeness, I feel that it doesn't hurt to post another approach to deal with the problem. The main difference is that I've chosen to use the average round-trip times of the ping command and provide a way to store the latency times and target host names in a group of variables that could be considered an array of structures.
Keep in mind that measuring round-trip latency with the ping command is not very accurate and the results can fluctuate tremendously. Referring to the following quote from Wikipedia:
Many software platforms provide a service called ping that can be used
to measure round-trip latency. Ping performs no packet processing; it
merely sends a response back when it receives a packet (i.e. performs
a no-op), thus it is a first rough way of measuring latency. Ping
cannot perform accurate measurements, principally because it uses the
ICMP protocol that is used only for diagnostic or control purposes,
and differs from real communication protocols such as TCP.
Furthermore, routers and ISP's might apply different traffic shaping
policies to different protocols.
#echo off
setlocal enabledelayedexpansion
set targets="192.168.0.1" "192.168.0.2"
for %%e in (%targets%) do (
call :GetAverageLatency %%e latency
if errorlevel 1 (
echo Unable to obtain latency from host: %%~e
exit /b
)
set /a count+=1
set "host[!count!].name=%%~e"
set "host[!count!].latency=!latency!"
if -!latency! gtr -!lowest! (
set lowest=!latency!
set index=!count!
)
)
echo;Mapping network drive to host: !host[%index%].name!
echo;
net use * "\\!host[%index%].name!"
set host
exit /b
:GetAverageLatency (__in hostName, __out *latency) {
for /f "skip=10 tokens=13 delims=m " %%e in ('ping "%~1"') do (
set "%2=%%e"
exit /b 0
)
exit /b 1
}
The set host command is obviously not necessary but it used to show the values stored in the host* variables.
this might have already been answered before but I couldn't find anything on that topic. Im trying to make a chat messenger in batch and for that I need to display the last line of a textfile. Here's what I tried (not very elegant):
#echo off
FOR /F %%x in (address.txt) DO set address=%%x
:A
IF NOT EXIST "%address%" GOTO A
GOTO B
:B
SET skipcount=1
:C
FOR /f "skip=%skipcount%" %%m in (%address%) DO ECHO %%m
SET m1=%%m
:D
FOR /f %%m IN (%address%) DO ECHO %%m > NUL
IF NOT %%m==%m1% SET skipcount=%skipcount%+1 GOTO D
GOTO C
This might work but I think it is full of mistakes for example syntax errors^^ So I am just trying to get a few hints to what is wrong:)
here's a pure batch utility (requires no external tools) that can shows a range of numbered lines
to show the last line use it like this:
call tailHead.bat -file=address.txt -end=1
You can use my JREPL.BAT regular expression text processing utility to create a tail command. JREPL.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.
The following command will show the last line in chat.txt
call jrepl "^.*" "" /match /inc -1 /f chat.txt
But there is a much better way to develop a batch chat program (assuming that is a worthwhile goal)
You can have a batch process in a loop, with input redirected outside the loop, and the loop will read and write newly added lines as they appear. You could use SET /P and ECHO, but it is simpler to use a single FINDSTR. This works because FINDSTR does not reset the file pointer when called, as explained at http://www.dostips.com/forum/viewtopic.php?p=9720#p9720.
You should use some command to suspend processing briefly within the display loop to prevent the loop from consuming 100% of a CPU core. You could use TIMEOUT, or the PING hack, but they introduce a ~1 second delay. I chose to use PATHPING to introduce a ~0.2 second delay.
Also, you must worry about preventing collisions if two processes write to the same text file simultaneously. This can be solved by using lock files, as explained at How do you have shared log files under Windows?.
Below is the beginning of a rudimentary batch chat program. It works by having two or more users each navigate to the same shared directory, and then run chat.bat sessionName, where sessionName is an agreed upon name for the shared chat file. Each user will get the shared chat dialog displayed in their master console window, and a new console window will open where they can write their contributions to the conversation. Enter :quit to exit the chat program.
#echo off
setlocal disableDelayedExpansion
if "%~1" equ ":input" goto :startInput
if "%~1" equ ":display" goto :display
set "base=%~1"
set "dialog=%base%.chat"
set "quitfile=%base%_%username%.chat.quit"
start "" "%~f0" :input
del "%quitfile%" 2>nul
cmd /c "%~f0" :display
del "%quitfile%" 2>nul
exit /b
:display
title Chat Dialog
set "quit="
if not exist "%dialog%" (call ) >>"%dialog%"
<"%dialog%" ( for /l %%N in () do (
if exist "%quitfile%" set "quit=1"
findstr "^"
if defined quit exit
pathping -p 150 -q 2 localhost >nul
))
:startInput
setlocal enableDelayedExpansion
title Chat Input
call :write ">>> %username% has joined the conversation"
:input
cls
set "text="
set /p "text=>"
if /i !text! equ :quit (
call :write "<<< %username% has left the conversation"
copy nul "!quitfile!"
exit
)
call :write
goto :input
:write
if "%~1" neq "" (set "text=%~1") else (set "text=%username%: !text!")
2>nul (
>>"!dialog!" (
echo(!text!
(call )
) || goto :write
)
exit /b
Still to be done:
Provide a mechanism to invite users to a chat.
Provide an option to clear chat contents at the beginning of a new chat session (in case an old sessionName is reused)
Provide a :list command to list the currently participating users. This would require creation of sessionNameUserId files that would remain locked as long as the user is still listening and/or participating. The display loop could receive a :list command via a file, the same way as I implemented :quit, and then it could attempt to open each sessionNameuserID file for writing. If it fails then the user is still active, and the name should be listed.
I'm sure there are other things that might be useful.