Windows Command Line - Select Server Based upon Ping Latency - windows

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.

Related

make a batch script foward an IP

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".

Windows: How to programatically connect to wireless networks?

With Windows 10 is it possible to setup up known networks and be able to connect to them without all the mouse movement and click?
Using Windows batch files, you can set it up to connect to networks you already know (Network1 or Network2, below) without ever touching the mouse.
#echo off
setlocal EnableDelayedExpansion
for %%i in ("Network1"
"Network2") do (
netsh wlan show networks mode=ssid | findstr /C:%%i
if !ERRORLEVEL! EQU 0 (
echo "Found %%~i - connecting..."
netsh wlan connect name=%%i
exit /b
) else (
echo "Did not find %%~i"
)
)
#echo on
Save the above to .bat and run it from cmd.exe or a program like Listary.
Some comments about the code:
If more than one of your listed networks are available, it will connect to whichever is first in the for loop list. You could also put the list in a file and change for %%i to for /F %%i
EnableDelayedExpansion and "!" around ERRORLEVEL
are needed to keep the variable ERRORLEVEL from being assigned
whatever it was at the beginning of the script. Since I don't
normally program Windows batch files, this is 2 hours of my life
gone that you won't have to deal with.
All the echoing is for debugging; the echo off at the top squelches it.
%% needed for variables in Windows batch files. The variable is referenced with % at the command line.
%%~i strips the quotation marks around the string when outputting to stdout.

How to retrieve ping data through a command line?

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

Batch ERRORLEVEL ping response

I'm trying to use a batch file to confirm a network connection using ping. I want to do batch run and then print if the ping was successful or not. The problem is that it always displays 'failure' when run as a batch. Here is the code:
#echo off
cls
ping racer | find "Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),"
if not errorlevel 1 set error=success
if errorlevel 1 set error=failure
cls
echo Result: %error%
pause
'racer' is the name of my computer. I'm having my computer ping itself so I can eliminate the variable of a poor connection. As I said before, the batch always results in failure. Oddly enough, the program works fine if I copy the code into the command prompt. Does anyone know why the program works fine in the command prompt but doesn't work as a batch?
Thanks
A more reliable ping error checking method:
#echo off
set "host=192.168.1.1"
ping -n 1 "%host%" | findstr /r /c:"[0-9] *ms"
if %errorlevel% == 0 (
echo Success.
) else (
echo FAILURE.
)
This works by checking whether a string such as 69 ms or 314ms is printed by ping.
(Translated versions of Windows may print 42 ms (with the space), hence we check for that.)
Reason:
Other proposals, such as matching time= or TTL are not as reliable, because pinging IPv6 addresses doesn't show TTL (at least not on my Windows 7 machine) and translated versions of Windows may show a translated version of the string time=. Also, not only may time= be translated, but sometimes it may be time< rather than time=, as in the case of time<1ms.
If you were to
echo "Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),"
you would see the % is stripped. You need to escape it as % has a special meaning within a batch file:
"Packets: Sent = 4, Received = 4, Lost = 0 (0%% loss),"
However its simpler to use TTL as the indication of success;
.. | find "TTL"
Testing for 0% loss may give a false positive, in this scenario:
Let's say you normally have a network drive on some_IP-address, and you want to find out whether or not it's on.
If that drive is off, and you ping some_IP-address, the IP address from which you ping, will respond:
Answer from your_own_IP-address: target host not reachable
... 0% loss
You might be better off using if exist or if not exist on that network location.
I 'm not exactly sure what the interaction between FIND and setting the error level is, but you can do this quite easily:
#echo off
for /f %%i in ('ping racer ^| find /c "(0%% loss)"') do SET MATCHES=%%i
echo %MATCHES%
This prints 0 if the ping failed, 1 if it succeeded. I made it look for just "0% loss" (not specifically 4 pings) so that the number of pings can be customized.
The percent sign has been doubled so that it's not mistaken for a variable that should be substituted.
The FOR trick serves simply to set the output of a command as the value of an environment variable.
Another variation without using any variable
ping racer -n 1 -w 100>nul || goto :pingerror
...
:pingerror
echo Host down
goto eof
:eof
exit /b
Yes ping fails to return the correct errorlevel. To check the network connection and the computer I used "net view computername" then checked %errorlevel% - simple and easy
First of all
>#echo off
>for /f %%i in ('ping racer ^| find /c "(0%% loss)"') do SET MATCHES=%%i
>echo %MATCHES%
Does not work. If it won't fail, it will detect 0%, because it has 0%.
If it fails, does not work either, because it will have 100% loss, which means, it found the 0% loss part behind the 10
10(0% loss)
Have it detect for 100% loss like so:
>for /f %%i in ('ping -n 1 -l 1 %pc% ^| find /c "(100%% loss)"') do SET check=%%i
Errorlevel might be a bit messed up, but it works like a charm:
>if '%check%'=='1' goto fail
>if '%check%'=='0' echo %pc% is online.&goto starting
1 means it failed
0 means it succeeded
In my script is use links.
Goto fail will go to :fail in my script which will message me that %pc% (which I'll have the user input in the beginning) is offline and will go for another run.
>:fail
>color 0c
>title %pc% is offline
>echo %pc% is offline
>PING -n 6 127.0.0.1>nul
>goto choice
I hope this helps.
The most simple solution to this I can think of:
set error=failure
ping racer -n 1 -w 100>nul 2>&1 && set error=success
Of course, -w needs to be adjusted if on a slow link (100ms might be too short over Dialup ;-))
regards
ping has an errorlevel output value. Success is 0, failure is 1.
Just do this:
C:\>ping 4.2.2.2
Pinging 4.2.2.2 with 32 bytes of data:
Reply from 4.2.2.2: bytes=32 time=28ms TTL=57
Reply from 4.2.2.2: bytes=32 time=29ms TTL=57
Reply from 4.2.2.2: bytes=32 time=30ms TTL=57
Reply from 4.2.2.2: bytes=32 time=29ms TTL=57
Ping statistics for 4.2.2.2:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 28ms, Maximum = 30ms, Average = 29ms
C:\>echo %errorlevel%
0
C:\>ping foo.bar
Ping request could not find host foo.bar. Please check the name and try again.
C:\>echo %errorlevel%
1
As you can see there is no need for all this scripting overkill.
Based on Alex K's note, this works for me on Windows 7:
#echo off
setlocal enableextensions enabledelayedexpansion
for /f %%i in (PCS.TXT) do (
SET bHOSTUP=0
ping -n 2 %%i |find "TTL=" > NUL && SET bHOSTUP=1
IF !bHOSTUP! equ 1 (
CALL :HOSTUP %%i
) else (
CALL :HOSTDOWN %%i
)
)
GOTO EOF
:HOSTUP
echo Host UP %1
GOTO EOF
:HOSTDOWN
echo Host DOWN %1
GOTO EOF
:EOF
exit /B
ping 198.168.57.98 && echo Success || echo failed
I liked the concept of the FIND in the ping results but why not just FIND the Reply from the Address being pinged?
In the example below I enter an IP address as a variable, PING that IP, then look for that variable in the reply string, using the FIND Command.
If the Reply String contains anything other than the correct IP it reports failure.
If you want you can just read the value of ERRORLEVEL from the FIND.
That will give you a reliable value to work with.
#echo off
Set /P IPAdd=Enter Address:
cls
ping %IPAdd% | find "Reply from %IPAdd%:"
if not errorlevel 1 set error=success
if errorlevel 1 set error=failure
cls
echo Result: %error%
pause
I needed to reset a wifi connection because it has issues. This was my quick solution.
#echo off
Rem Microsoft Windows 10 ping test to gateway.
Rem Run batch file from an administrative command prompt.
cls
:starting
Rem Send one ping to the gateway. Write the results to a file.
ping 192.168.1.1 -n 1 > pingtest.txt
Rem Search for unreachable in the file.
c:\windows\system32\findstr.exe "unreachable" pingtest.txt
Rem errorlevel 0 reset the adapter if 1 then wait 10 minutes and test again
if %errorlevel%==1 goto waiting
Rem unreachable was found reset the adapter.
Rem write the date and time the reset was done.
echo Reset date: %date% time: %time% >> resettimes.txt
Rem issue netsh interface show interface to find your adapter's name to reset
Rem my adapter is "wi-fi"
netsh interface set interface "wi-fi" disable
timeout /t 5
netsh interface set interface "wi-fi" enable
:waiting
echo "It is online waiting 10 minutes"
timeout /t 600
goto starting

Flow control in a batch file

Reference Iterating arrays in a batch file
I have the following:
for /f "tokens=1" %%Q in ('query termserver') do (
if not ERRORLEVEL (
echo Checking %%Q
for /f "tokens=1" %%U in ('query user %UserID% /server:%%Q') do (echo %%Q)
)
)
When running query termserver from the command line, the first two lines are:
Known
-------------------------
...followed by the list of terminal servers. However, I do not want to include these as part of the query user command. Also, there are about 4 servers I do not wish to include. When I supply UserID with this code, the program is promptly exiting. I know it has something to do with the if statement. Is this not possible to nest flow control inside the for-loop?
I had tried setting a variable to exactly the names of the servers I wanted to check, but the iteration would end on the first server:
set TermServers=Server1.Server2.Server3.Server7.Server8.Server10
for /f "tokens=2 delims=.=" %%Q in ('set TermServers') do (
echo Checking %%Q
for /f "tokens=1" %%U in ('query user %UserID% /server:%%Q') do (echo %%Q)
)
I would prefer this second example over the first if nothing else for cleanliness.
Any help regarding either of these issues would be greatly appreciated.
Again, there are multiple things to note here.
if errorlevel
The help for if says:
IF [NOT] ERRORLEVEL number command
as syntax for the if errorlevel condition. That is, you must provide a number to compare against. Keep in mind that if errorlevel n evaluates to true if the exit code was at least n.
So
if errorlevel 1 ...
catches any error (that is signaled through the exit code), while
if errorlevel 0 ...
simply is always true.
Anyways, you probably want a
if not errorlevel 1 ...
here, since that condition is true if no error occurred.
Skipping lines
The for /f command has an argument skip=n which can be used to skip lines at the start. If your output starts with two lines you don't want, then you can just do
for /f "skip=2 tokens=1" %%Q in ('query termserver') do
Iterating over multiple known values in for /f
The problem with your second code snippet is that for iterates line-wise. So when you give it a single environment variable it will tokenize it (and put the tokens into different variables), but the loop runs only once per line. Also note that using set here is a bit error-prone as you might get more back than you want. Something like
for /f ... in ("%TermServers%") ...
would have been easier. Still, that doesn't solve the original problem. The easiest way to solve this would probably be something like the following:
rem space-separated list of servers
set TermServers=Server1 Server2 Server3 Server7 Server8 Server10
rem call the subroutine with the list of servers
call :query_servers %TermServers%
rem exit the batch file here, to prevent the subroutine from running again afterwards
goto :eof
rem Subroutine to iterate over the list of servers
:query_servers
rem Process the next server in the list
rem Note the usage of %1 here instead of a for loop variable
echo Checking %1
for /f "tokens=1" %%U in ('query user %UserID% /server:%1') do (echo %%Q)
rem Remove the first argument we just processed
shift
rem if there is still another server to be processed, then do so
rem we're mis-using the subroutine label as a jump target here too
if not [%1]==[] goto query_servers
rem This is kind of a "return" statement for subroutines
goto :eof
(untested, but should work.)
ETA: Gah, and once again I miss the most obvious answer:
set TermServers=Server1 Server2 Server3 Server7 Server8 Server10
for %%S in (%TermServers%) do (
for /f "tokens=1" %%U in ('query user %UserID% /server:%1') do (echo %%Q)
)
Note that this is simply for, not for /f and it will dutifully iterate over a list of values. I don't know how I missed that one, sorry.
NT shell/batch language is not smart enough to accept IF NOT ERRORLEVEL (... -- you need to do an explicit comparison, like this:
if not %ERRORLEVEL%==0 (
...

Resources