Batch file 'continue' to next iteration in FOR loop - windows

I'm currently supporting a team of techs who are upgrading machines in offices and I need to keep track of the amount of machines that are online.
Currently what I have is a text file (OfficeName.txt) with a list of machine names, and a batch file (OfficeName.bat).
What I want to happen is for it to loop through the list of machines and ping them.
If the ping is successful, remove the name from the list and increment a counter by 1, if unsuccessful, then move on to the next machine in the list.
The issue I'm having is that if a machine's ping result comes back with "could not find host", it still sets the errorlevel to 0, so I can't use an IF/ELSE.
My current attempt looks like this:
#echo off
setlocal EnableDelayedExpansion
set /a counter=0
set "NVC="
for /F %%a in (%~n0.txt) do set "NVC=!NVC! %%a"
:ping
for %%i in (%NVC%) do (
ping %%i -n 1 >nul | find "TTL=" >nul || echo. %%i is offline.
set /a counter+=1
echo %%i is online
set "NVC=!NVC: %%i=!"
)
cls
echo. %counter% machines are online.
if defined NVC goto :ping
echo All machines in %~n0 are online.
pause
The problem is that once the "%%i is offline" line is done, it just continues to the next line and removes it anyway.
Is there a way to skip the 3 lines below the ping if the ping result is bad, and continue with the next iteration in the list?
Note: I'm running this from Server 2008, pinging Win 8.1 machines.

Your first problem is that you're trying to find "TTL=" in output that's been redirected to NUL. Remove that redirection so FIND gets something to search.
Your second problem is a logic error, where even if you execute the || clause, the other lines are executing too. You need to use an ELSE or another IF clause.
Finally, I think you're overcomplicating the processing of your list of IP addresses.
Try this, and see if it makes sense.
#echo off & setlocal
set /a counteron=0
set /a counteroff=0
for /f %%i in (%~n0.txt) do (
ping %%i -n 1 | find "TTL=" >nul
if errorlevel 1 (
set /a counteroff+=1
echo %%i is offline.
) else (
set /a counteron+=1
echo %%i is online
)
)
echo.
echo %counteron% machines are online.
echo %counteroff% machines are offline.

Related

Need to run a command when I get a ping response

The idea is that a new device enters my network and a script running in the background can run a command to back it up, SyncToy for example (cd Program Files/SyncToy SyncToyCmd.exe -r).
I've run into scripts from similiar questions (How to check if ping responded or not in a batch file)
#setlocal enableextensions enabledelayedexpansion
#echo off
set ipaddr=%1
set oldstate=neither
:loop
set state=down
for /f "tokens=5,7" %%a in ('ping -n 1 !ipaddr!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," set state=up
)
if not !state!==!oldstate! (
echo.Link is !state!
set oldstate=!state!
)
ping -n 2 127.0.0.1 >nul: 2>nul:
goto :loop
endlocal
However I can't figure out how to run the command when the state changes to "up" as an exception in the loop, I also only want to run it once, adding a condition of once every 12 hours maybe.
Is this as simple as placing the command inside the block that tests for receipt? I do not understand how SyncToy would know the IP address.
for /f "tokens=5,7" %%a in ('ping -n 1 !ipaddr!') do (
if "x%%a"=="xReceived" if "x%%b"=="x1," (
set state=up
PUSHD "%ProgramFiles%/SyncToy"
SyncToyCmd.exe -r
POPD
)
)

CMD: search IP's of devices with special MAC

I need to find IP of device or devices in local network. I only know that their MAC address should start with "xx-xx-xx-xx-". I found following script that do almost what I need:
#echo off
:top
:: Loop through arp table entries and look for my device's MAC address
for /f "tokens=1-5 skip=3" %%f in ('arp -a') do (
if "%%g"=="xx-xx-xx-xx-xx-xx" set ip=%%f
)
if "%ip%"=="" (
echo Discovering network...
:: Ping all IPs from 192.168.0.1 to 254
for /L %%N in (1,1,254) do start /b ping -n 1 -w 200 192.168.0.%%N >nul
timeout 1 >nul
goto :top
) else (
echo Device found found: %ip%
)
pause
But this script search only with full MAC and only one device. How make it search subMAC and several devices?
the following works for me:
#echo off
:top
:: Loop through arp table entries and look for my device's MAC address
set "ip="
for /f "tokens=2" %%f in ('arp -a^|find " d4-85-64"') do set ip=%%f
if "%ip%"=="" (
echo Discovering network...
:: Ping all IPs from 192.168.0.1 to 254
for /L %%N in (1,1,254) do start /b ping -n 1 -w 200 192.168.0.%%N >nul
timeout 1 >nul
goto :top
)
echo Device found: %ip%
pause
Changes:
set "ip=" to empty variable (in case, it already exists (from previous run))
removed skip (not needed),
set "tokens=2" (that's all, we need)
included find " xx-xx-xx" to search the desired line (with leading space for "start with") (of course I choosed a MAC existing in MY network)
moved success message out of if (not neccesary, but cleaner code)

Windows Command Prompt print text file with delay

I have no experience with command prompt whatsoever, but I'd like to make a batch script (for fun and learn) that would print a text file from a given location, line by line, with a 1 second delay.
I would also want it to be able to pause/unpause when I press a designated key (ex: space) and feed me an extra line (on top of those already programmed to run) when I press another key (ex: enter).
I know I can add a 1 second delay by pinging localhost ping -n 5 127.0.0.1 > nul
And I know I can see the content of a text file using more text.txt, but I don't know how to iterate through an entire text file until EOF is met and I don't know how to pause/resume and feed extra line.
Hope it doesn't sound stupid or out of scope in this context, but it's just something that interests me right know and I know a lot people here have the knowledge to do this.
1) If you have experience in programming, you will know using a for loop is the most common way to do things one by one, e.g. line by line.
2) You can simply use ping localhost -n 2 >nul for 1 second delay, the 2 in the ping is not indicating 2 seconds, but 1 second instead. (I have no idea about that, just get used to it)
3) You can't pause/unpause when cmd is pinging, I mean there's no way to force the program to pause/unpause because the delay process is executed in just a line of code! Or you can magically add some code into it like ping localhost -n 2 pause while(KeyDown(SPACE)) >nul (just kidding :) )
4) Extra lines? Hmm... Remember batch is not a powerful language so... Yeah
Here is a simple code to print text line by line each second in a .txt file
for /f %%a in (your_text.txt) do (
echo %%a
ping localhost -n 2 >nul
)
You could do it synchronously with choice /t 1 (for a 1-second timeout) and some key other than Spacebar. Perhaps P for Pause?
#echo off
setlocal
set "textfile=notes.txt"
echo Hit P to pause / resume, Q to quit.
echo;
for /f "tokens=1* delims=:" %%I in ('findstr /n "^" "%textfile%"') do (
echo(%%J
choice /t 1 /c €pq /d € >NUL
if errorlevel 3 exit /b 0
if errorlevel 2 (
pause >NUL
ping -n 1 -w 750 169.254.1.1 >NUL
)
)
exit /b 0
Unfortunately, choice only allows a-z, A-Z, 0-9, and extended characters 128-254. There's no way to make it listen for Enter or Space. And choice is the only Windows command of which I'm aware that'll accept a single keypress and do something meaningful based on which key was pressed.
I think you'll have to use some sort of compiled language (or possibly PowerShell with a .NET class?) to listen for keypress events on the console. You could probably do it in JavaScript, but you'd have to display your output in a web browser or HTA window.
A "scrolling editor"? It is a crazy idea, isn't it? I LIKE IT! ;-) I adopted your project and add some points...
#echo off
rem ScrollEditor.bat: "dynamic" very simple line editor
rem Antonio Perez Ayala aka Aacini
if "%~1" neq "" if "%~1" neq "/?" goto begin
echo ScrollEditor.bat filename.ext
echo/
echo File lines will be continually scrolling, one per second.
echo/
echo You may pause the scroll via P key. In the "paused" state, the last displayed
echo line is named "current line", and the following commands are active:
echo/
echo #L Return/advance the listing to line #; continue the scroll from there.
echo [#]D Delete [from previous line # up to] current line.
echo I Insert lines after current line; end insert with *two* empty lines.
echo P End "paused" state; continue the scroll from current line on.
echo E End edit and save file, keep original file with .bak extension.
echo Q Quit edit, not save file.
goto :EOF
:begin
if not exist %1 echo File not found & goto :EOF
rem Load file lines into "line" array
set /P "=Loading file... " < NUL
setlocal DisableDelayedExpansion
for /F "tokens=1* delims=:" %%a in ('findstr /N "^" %1') do (
set "line[%%a]=%%b"
set "lastLine=%%a"
)
echo last line: %lastLine%
echo To pause scrolling, press: P
echo/
setlocal EnableDelayedExpansion
set "validCommands=LDIPEQ"
set currentLine=1
:command-P End "paused" state
:ScrollLine
if %currentLine% gtr %lastLine% (
set "currentLine=%lastLine%"
echo EOF
goto GetCommand
)
set "num= %currentLine%"
echo %num:~-4%: !line[%currentLine%]!
set /A currentLine+=1
choice /C PC /N /T 1 /D C >NUL
if errorlevel 2 goto ScrollLine
rem Enter paused state
set /A currentLine-=1
:GetCommand
echo/
set /P "command=Command [#L,#D,I,P,E,Q]? "
set "letter=%command:~-1%"
if "!validCommands:%letter%=!" equ "%validCommands%" goto GetCommand
goto command-%letter%
:command-L Go to line #; continue scrolling
set "currentLine=%command:~0,-1%"
goto ScrollLine
:command-D Delete from line # to current line
set "prevLine=%command:~0,-1%"
if not defined prevLine set "prevLine=%currentLine%"
rem Move lines after last deleted one into deleted lines
set /A currentLine+=1, newCurrent=prevLine-1, lines=currentLine-prevLine
for /L %%j in (%currentLine%,1,%lastLine%) do (
set "line[!prevLine!]=!line[%%j]!"
set /A prevLine+=1
)
set /A currentLine=newCurrent, lastLine=prevLine-1
if %currentLine% equ 0 set "currentLine=1"
echo %lines% line(s) deleted (current=%currentLine%, last=%lastLine%)
goto GetCommand
:command-I Insert lines after current one
echo End insert with *two* empty lines
echo/
rem Read new lines into "ins" array
set "newLine=%currentLine%"
:insertLine
set "line="
set /A newLine+=1
set "num= %newLine%"
set /P "line=+%num:~-3%: "
set "ins[%newLine%]=!line!"
rem The most complex part: end in two empty lines...
if not defined line (
set /A newLine+=1
set "num= !newLine!"
set /P "line=+!num:~-3!: "
if defined line (
set "ins[!newLine!]=!line!"
) else (
set /A newLine-=2
)
)
if defined line goto insertLine
rem Move old lines to new place to make room for new lines
set /A lines=newLine-currentLine, currentLine+=1, newLast=lastLine+lines
for /L %%j in (%lastLine%,-1,%currentLine%) do (
set "line[!newLast!]=!line[%%j]!"
set /A newLast-=1
)
rem Insert new lines in old place
for /L %%j in (%currentLine%,1,%newLine%) do set "line[%%j]=!ins[%%j]!"
set /A lastLine+=lines, currentLine=newLine
echo %lines% line(s) inserted (current=%currentLine%, last=%lastLine%)
goto GetCommand
:command-E End edit, save file
echo Saving file...
move /Y %1 "%~N1.bak"
(for /L %%i in (1,1,%lastLine%) do echo(!line[%%i]!) > %1
:command-Q Quit edit
echo End edit
This program have multiple problems: don't check for valid input in commands, may have problems with special Batch characters and if the first character of a line is a colon, eliminate it. However, it is a good starting point for this project!
Perhaps you may be interested in this similar project.

Ping particular host and move the results to file with current time stamp for every hour

I am trying create a batch file which pings a particular host and moves the results to a file with the current time stamp for every hour. I was able to do it continuously, but the I want to know whether there is any script with which I can set the interval for which it runs the ping command.
I am successfully able to print the time stamp, ping response and the able to change the name of the file with current time stamp, but it's happening continuously. I want to do it for a time interval like say for 4hrs continuous ping then move the results to a file.
please help
There is no sane way to do this with a command script only.
What I recommend is to write a script that performs the requested action once.
Use Windows Task Scheduler to call the script every hour.
What I've attempted is by NO means an ideal, much less precise method of doing what you desire (purely in batch), however with some tweaking and refining, or by serving as a potential route/template for similar scripts by more experienced batch scripters, I believe it may be useful:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1-2 delims=:" %%i in ('echo %time%') do (
set /a hour=%%i+4
set /a minute=%%j
set expected=!hour!:!minute!
echo !expected!
)
for /f "tokens=*" %%i in ('ping 192.168.1.1 -n 1') do (echo %%i>pinglog.txt && goto PING)
:PING
for /f "tokens=1-2 delims=:" %%i in ('echo %time%') do (
set current=%%i:%%j
echo !current!
)
if !current!==!expected! (
goto EOF
) else (
for /f "tokens=* skip=2" %%i in ('ping 192.168.1.1 -n 2 -w 30000') do (
echo %date% %time% %%i>>pinglog.txt && goto PING
)
)
Control the hour or minute interval by adding the relevant value to either, in the lines:
set /a hour=%%i+4 rem Here I've added 4 hours
set /a minute=%%j
Remove the echo !expected! and echo !current! lines if you don't wish to see the temporal progression in the cmd output, which can be minimised while the script is running.

How to check if a string stored in a variable is the same as an ip address from netstat -nao in batch

This is my first time asking a question so try not to be to hard on me.
I am competing in a small capture the flag event and am writing some simple batch scripts to help out a bit.
The purpose of program is to check if output from netstat -nao is in the dontkill variable. If it is, then don't kill it; if it is not, then look for the process id and kill the connection. dontkill will contain my IP address so that it doesn't kill my remote connection to the computer.
Seems pretty simple, but I have had major trouble because I don't know how to write in batch very well.
Here is my code so far:
for /L %%i in (1,0,) do #for /f "tokens=5" %%j in ('netstat -nao ^| findstr ^"ESTABLISHED^"' ) do #taskkill /f /PID %%j
This goes through and looks for any ESTABLISHED connections and kills them in an infinite loop. I have tried many different variations of this command, and I recognize that I need some sort of a if/else statement in the second do to check if dontkill is the same as what is stored in the variable %%a in the loop:
for /f "tokens=3" %%a in('"netstat -nao^|findstr ^"ESTABLISHED^"')
I know this would probably be easier in some other scripting language but it has to be batch because the computers I will be using it on will be fresh install Windows XP, Server 2003, 2008 and Win7.
Ok with the for loop directly above the variable a is carrying output like this "192.168.1.230:1003" so it has the ip address and also the port separated by a colon.
I need to be able to compare it to the variable dontkill which only has the Ip address.
Is there any way to get rid of the :1003 so that I can compare it to the variable dontkill.
I have been messing around with things such as
SET line="192.168.1.241:10256"
SET string3=%line::[0-9]*=%
but it doesn't work.
So to be clear I'm trying to go from "192.168.1.241:10256" to "192.168.1.241" so that I can compare the final result to dontkill and decide what to do.
Thanks for the help in advance.
Sudo code
///////////////////////////////////////////////////////////////////
DONTKILL = "192.168.1.241"
FOR(NUMBER OF PROCESSES FILTERED BY FINDSTR. LOOK AT COLUMN 3 AND 5)
VAR A IS THE COLUMN 3 INFO which is some ip address with port "192.168.1.241:1003"
VAR B IS THE COLUMN 5 INFO which is PID "1524"
IF(DONTKILL == A)
DONTKILL THAT PROCESS
ELSE
KILL THAT PROCESS USING
TASKKILL /F /PID B
I got the inspiration for this method from http://blog.commandlinekungfu.com/2010/01/episode-76-say-hello-to-my-little.html
Like this :
#echo off&cls
setlocal EnableDelayedExpansion
set "DONTKILL=127.0.0.1"
for /f "tokens=1-5 delims= " %%a in ('netstat -nao ^| find /i "ESTABLISHED"' ) do (
echo [%%a] [%%b] [%%c] [%%d] [%%e]
call:GetIP %%c
if !$IP!==%DONTKILL% (
echo IP !$IP! IS EQUAL TO DONTKILL [%DONTKILL%]
echo.
) else (
echo IP !$IP! IS NOT EQUAL TO DONTKILL [%DONTKILL%]
echo.
)
)
exit/b
:GetIP
for /f "tokens=1 delims=:" %%a in ('echo %1') do (
set "$IP=%%a"
goto:eof)

Resources