I'm trying to create a little batch file that checks multiple PCs read from a text file. For any PCs it finds are pingable, it writes a line in a "results" text file saying so. Here's what I've got:
#Echo off
set file=C:\logs\registercheck.txt
date /t >%file%
FOR /F %%I IN (C:\work\regnames.txt) DO (ping /n 1 %%I | ping /n 1 %%I | IF errorlevel 1 goto :nextreg | echo %%I is still on and has not been powered off! >>%file% | :nextreg)
PAUSE
So...when I run the file, I get multiple lines of "goto was unexpected at this time" and the only thing written in my output text file is the date. What am I doing wrong?
Thank you!
#Echo off
setlocal enableextensions disabledelayedexpansion
set "logFile=C:\logs\registercheck.txt"
set "inputFile=C:\work\regnames.txt"
>>"%logFile%" date /t
for /f "usebackq delims=" %%i in ("%inputFile%") do (
ping -n 1 %%i >nul 2>nul
if not errorlevel 1 (
>>"%logFile%" echo(%%i is still on and has not been powered off!
)
)
You have two errors.
The first is that to put all the commands in a single line, the separator is not the pipe character (|) but the ampersand (&)
The second is that inside the do code block of the for command, if one goto is executed, the for command is finished, independently of where the label is placed. And labels inside for code blocks usually generate errors (depends of its position).
If instead of the previous code, you want a single line loop, it can be written as
for /f "usebackq delims=" %%i in ("%inputFile%") do ( ping -n 1 %%i >nul 2>nul & if not errorlevel 1 >>"%logFile%" echo(%%i is still on and has not been powered off! )
or
for /f "usebackq delims=" %%i in ("%inputFile%") do ( ping -n 1 %%i >nul 2>nul && >>"%logFile%" echo(%%i is still on and has not been powered off! )
that makes use of the && construct. It is intended as a shortcut for the if not errorlevel 1 .... If the command at the left of the && does not raise an errorlevel, then the command on the right side is executed.
This for the batch sintax. Now the ping. There is a difference in how ping command behaves depending of the ip version. It is not the same to ping an ipv4 address than to ping an ipv6 address. If needed you can grab from here a subrotine to handle the differences.
Related
I want to get particular exit code for each output in for loop which I am printing(echo). I want to use that exit code and give particular output.
for /f "tokens=* delims=" %%i in (list.txt) do ping -n 3 %%i >nul && if /i "ERRORLEVEL" == "0" (
echo %%i Alive
) else (
echo %%i Failed
)
There is no need to get the errorlevel by literally specifying it for each result. The exitcode/errorlevel can be evaluated by using conditional operators && and ||
So all you need is:
#echo off
for /f "usebackq delims=" %%i in ("list.txt") do ping -n 3 %%i >nul 2>&1 && echo %%i Alive || echo %%i Failed
What happens is simple. The errorlevel or exit code is evaluated. If it the errorlevel is 0 it is a success and it will use the && operator to fire the next command. if it is 1 or larger, it will constitute as an error and it will use the or operator || and perform the relevent command.
If however you MUST use the errorlevel or %errorlevel literally:
#echo off
for /f "usebackq delims=" %%i in ("list.txt") do (
ping -n 3 %%i >nul 2>&1
if errorlevel 1 (
echo %%i Failed
) else (
echo %%i Alive
)
)
Obviously without needing delayedexpansion
The exact answer to your problem, as you stated it, is this:
#echo off
setlocal EnableDelayedExpansion
for /f "delims=" %%i in (list.txt) do ping -n 3 %%i >nul 2>&1 & if "!ERRORLEVEL!" == "0" (
echo %%i Alive
) else (
echo %%i Failed
)
for /f "tokens=* delims=" %%i in (list.txt) do (
set "reported="
ping -n 3 %%i >nul
call echo %%i responded with errorlevel %%errorlevel%%
if errorlevel 2 set "reported=y"&echo errorlevel 2 actions&cd .
if errorlevel 1 set "reported=y"&echo errorlevel 1 actions&cd .
if not defined reported echo errorlevel 0 actions
)
The string "ERRORLEVEL" will never be equal to the string "0". You are looking for the value of errorlevel, which would normally be %errorlevel% BUT since you have used a code block (a parenthesised sequence of statements) %errorlevel%, like any variable would be replaced by its value at the time the code block is encountered, not as it changes when executed. See Stephan's DELAYEDEXPANSION link: https://stackoverflow.com/a/30284028/2128947
You can use if errorlevel? as shown, but you must interrogate errorlevel in reverse-order as if errorlevel n is true for errorlevel being n or greater than n.
The code shown will set errorlevel to 0 (using cd .) after whatever actions you choose to execute so any change to errorlevel made by those actions is overridden and will not cause any of the other errorlevel actions to be executed. If none of the actions is executed, reported will remain undefined, so the fact that it's undefined tells us that the original errorlevel was 0.
I'm stuck on this one, I've managed to create a batch file that imports list of IPs & hostnames but when I test for success or failure, I'd like to output how long it took. I don't know how to capture this and include it in the output. I'm trying to hide the default ping.exe output. Here's my code
#echo off
for /F "tokens=1,2 delims= " %%A in (computers.txt) do (
rem ping -n 1 %%A | findstr /i "TTL=" | find "Reply" > nul
if %errorlevel% == 0 (
echo %%B %%A successful ping in ?? Seconds
echo %%B %%A successful ping [%date%, %time%] >> log.txt
)
)
Pause
I've figured it out:
#echo off
setlocal enableDelayedExpansion
for /F "tokens=1,2 delims= " %%A in (computers.txt) do (
for /f "tokens=7 delims== " %%G in ('ping -n 1 %%A') do (
ping -n 1 %%A >nul
if errorlevel 1 (
echo %%B %%A unsuccessful ping
echo %%B %%A unsuccessful ping [!date!, !time!] >> log.txt
) else if "%%~G" NEQ "of" if "%%~G" NEQ "0" (
set ms=%%~G
set ms=!ms:ms=!
for /f %%Z in ('powershell !ms!/1000') do set second=%%~Z
echo %%B %%A successful ping in !second! seconds
echo %%B %%A successful ping [!date!, !time!] >> log.txt
)
)
)
Pause
Using FOR /F, it is possible to process command output.
As you may see, I have 2 ping -n 1 %%A. It is because FOR /F doesn't capture the ERRORLEVEL generated by the processing command. The first command gets the output, the second gets ERRORLEVEL.
if "%%~G" NEQ "of" if "%%~G" NEQ "0" filters out the unneeded results.
The powershell command converts milliseconds to second(batch file does not support decimal numbers)
I/O example
I don't have any other computers on my network, therefore I tested it with URLs.
Input -> Output
www.google.com A A www.google.com successful ping in 0.002 seconds
www.yahoo.com B B www.yahoo.com successful ping in 0.032 seconds
a.a.a C C a.a.a unsuccessful ping
Checking for XP via the registry using this script in batch.
#echo off
pushd %~dp0
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && set OS=XP || set OS=NEWER
echo %os%
pause
)
in pxhost I have the list of pc's to check for xp and then do something, or not.
However I cant get the above to work. The variable is never set and it just echos back "windows_NT". If, however, I take the for loop out and run the reg query without the variable:-
reg query "\\xp-4c54fa50d0da\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && set OS=XP || set OS=NEWER
the echo works and reports "XP", or "newer" if I change "5.1" to "5.7" for test purposes.
What's going on? Thanks.
thanks for answers but now I have more problems
From here I can now echo the right responses but the calling doesn't work at all. I have an XP, win 7 32 and win 10 64 in test in the text file pxhosts.
XP is first in the list and gets ignored even when it is echo'd back correctly. something is stopping the calling from happening. Really driving me mental this lol.
I am trying to write a remote permissions script that applies to either XP filr system or 32 or 64 (newer windows). The total code is below:-
#echo off
setlocal enabledelayedexpansion
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && set pctype=XP || set pctype=NEWER
cls
echo !pctype!
pause
if !pctype! == !XP! ( call :XP !%%A!)
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set arc=32 || set arc=64
cls
echo !arc!
pause
if !arc! == !64! ( call :64 !%%A!)
if !arc! == !32! ( call :32 !%%A!)
)
:32
echo 32 okay
pause
icacls "\\%1\C$\ProgramData\folderFoo" /T /C /grant(:r) "Domain Users":(OI)(CI)(F) /inheritance:e >> "%~dp0%1.txt" 2>&1
pause
rem return from a subroutine
exit /B
:64
echo 64 okay
pause
icacls "\\%1\C$\Program Files (x86)\Folderfoo" /T /C /grant(:r) "Domain Users":(OI)(CI)(F) /inheritance:e >> "%~dp0%1.txt" 2>&1
pause
rem return from a subroutine
exit /B
:XP
echo xp okay
pause
CACLS "\\%1\C$\Documents and Settings\All Users\Application Data\Folderfoo" /E /T /C /G "Domain Users":F >> "%~dp0%1.txt" 2>&1
pause
rem return from a subroutine
exit /B
new edit 201216
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL
IF %ERRORLEVEL%==0 set pctype=XP
IF %ERRORLEVEL%==1 set pctype=NO
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL
IF %ERRORLEVEL%==0 set arc=32
IF %ERRORLEVEL%==1 set arc=64
IF !pctype!==XP (call :xp %%A)
IF !pctype!==NO IF !arc!==32 (call :32 %%A)
IF !arc!==64 (call :64 %%A)
)
This is now picking up the 64 or 32 using reg query, however because an XP machine will always be a 32 bit machine it keeps running the code for newer windows PC with 32 bit as well. My subroutines for XP are dealing with docuemnts and settings folder, newer 32 machines just the user folder format.
I need to run a subroutine on XP machines, 32 bit newer machines and 64 bit machines. At the moment xp code runs on 32 bit machines and vice versa because they share 32 bit architecture.
I have tried above to eradicate the problem by only running 32 bit routine if the pc is newer with a 32 bit file structure by way of variables (2nd line of IF's). Thanks :)
If the thingy works without the for-loop and the variable is not set within a for-loop consider a search for something like Batch variable not set in for-loop and boom there will be a plenty of questions like yours.
Answer is the same for all:
Use Delayed Expansion!
To use that, add the line setlocal EnableDelayedExpansion to the beginning of the batch-file and whereever you need a variable in a closed set of parenthesis like a for-loop or an if-condition change %myVar% to !myVar!.
Example to verify:
#echo off
setlocal EnableDelayedExpansion
set foo=foo
if 1==1 (
set foo=bar
echo Not delayed: %foo%
echo Delayed: !foo!
) ELSE (
echo If you land here something went heavily wrong...
)
Reasoning:
In batch closed sets of parenthesis are calculated when the beginning is reached. In the above example that means that when the program reaches if 1==1 it knows the value of foo as "foo" eventhough I changed it seemingly before.
An alternative is to use
call echo %%myVar%%
In your example however you would not even need that variable... In the same positions where you set the value of OS you could as well simply echo it:
... && set OS=XP || set OS=NEWER -> ... && echo XP || echo NEWER
Edit after big question edit:
You only and really only need exclamation marks when accessing a variable that got set within the loop! You made two major mistakes I spotted so far:
1) For-Loop variables are set in the loops header and with that are an exception from this rule: %%A is the correct way of accessing it and not !%%A!
2) If you want to make a string comparison like if varValue == thisString you should NOT surround the string to check with exclamation marks:
if !arc!==64 should do the trick here!
Else the comparison would look like if 64==!64! which is not the desired behaviour I guess.
Read your questions edit closer again:
Something is stopping the calling from happening
Exactly the problem with the if described above in point 2 :)
Edit after question for another method to find the queried values remotely:
After a short search and playing around a bit with the results I came up with this (Windows 7+ required on executing machine; tested with Windows 10 Pro):
#echo off
setlocal EnableDelayedExpansion
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
for /f "tokens=2 delims=:" %%B in ('systeminfo /s "%%~A" ^| findstr /i "Systemtype"') do (
call :removeLeadingSpaces "%%~B" type
echo !type!
)
for /f "tokens=2 delims=:" %%C in ('systeminfo /s "%%~A" ^| findstr /i "Operatingsystemname"') do (
call :removeLeadingSpaces "%%~C" osName
echo !osName!
)
)
pause
Goto:eof
:removeLeadingSpaces
for /f "tokens=* delims= " %%f in ("%~1") do set %2=%%f
Explanation:
Reads the computernames from the file as it already does. Takes the second part from the output of the command systeminfo /s <computername> queried for the specific strings "Systemtype" and "Operatingsystemname". The ^ is there to escape the piping symbol |. It then sends the string to a subfunction that truncates the leading spaces and sets them to the specified variable.
NOTE: The strings used to query may vary for a different language setting! Those above are freely translated from my German OS! To check how they are for your language setting go ahead and open a commandprompt and type systeminfo and hit Enter Now look for the ones you need.
Systemtype outputs the processor architecture and OSname the name of the operating system (who would have thought that ey?). If you have found what you need change the strings from my example to your needs.
From where it says echo !type! and echo !osName! do whatever you want with those variables. Any questions left? Feel free to ask!
Edit after chat conversation
Your problem (I think) is here:
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL && set arc=32 || set arc=64
IF ERRORLEVEL==0 set arc=32
IF ERRORLEVEL==1 set arc=64
When using the way of processing the outcome of the find directly using && echo positive result || echo negative result the errorlevel will always reside at 0! So you got two ways of handling this:
Simply delete the errortype handling
Delete the part && set arc=32 || set arc=64 so the errorlevel is set accordingly to the outcome of the command as it is already done at the check for XP
You can test this on your own in the commandline with the queries from your question:
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL
echo %ERRORLEVEL%
This should echo 1 if you do not have an XP-computer (what I hope for you ;) ). This is because find could not find the string specified -> falsey value.
Now reset the errorlevel:
set ERRORLEVEL=0
And try this one:
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL && echo should be 0 || echo should be 1
Would be the version without direct errorlevel handling but with acting according to the direct result of the find command.
Now do echo %ERRORLEVEL% You will notice that it is 0 eventhough the above command should have returned a falsey value.
Edit after finding the mistake in chat:
In the end it was one lethal space too much... ONE SPACE screwing up parts of the script.
Base script:
#echo off
setlocal EnableDelayedExpansion
pushd %~dp0
for /f "usebackq tokens=*" %%A in ("%~dp0pxhosts.txt") do (
reg query "\\%%A\HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion" | find /i "5.1" > NUL
IF !ERRORLEVEL!==0 set pctype=XP
IF !ERRORLEVEL!==1 set pctype=NOXP
CLS
reg query "\\%%A\HKLM\Hardware\Description\System\CentralProcessor\0" | find /i "x86" > NUL
IF !ERRORLEVEL!==0 set arc=fs1
IF !ERRORLEVEL!==1 set arc=fs2
CLS
IF !pctype!==XP (call :XPFS %%A)
IF !pctype!==NOXP IF !arc!==fs1 (call :type1 %%A)
IF !arc!==fs2 (call :type2 %%A)
)
Goto :eof
:XPFS
REM Do things for XP computer
Goto :eof
:type1
REM Do things for 32-bit computer
Goto :eof
:type2
REM Do things for 64-bit computer
Goto :eof
My GPS device sends on COM port the NMEA infos.
I want to extract the time info and set the system time.
In a batch I wrote:
type COM2 | find "GPRMC"
brings me the required info - but continously.
Example (first 3 lines):
$GPRMC,100211.279,V,4816.1496,N,01623.0965,E,0.00,0.00,280316,,,N*7A
$GPRMC,100212.279,V,4816.1496,N,01623.0965,E,0.00,0.00,280316,,,N*79
$GPRMC,100213.279,V,4816.1496,N,01623.0965,E,0.00,0.00,280316,,,N*78
So the next batch command is never executed, because the GPS device is sending as long as it's on.
I want to extract the second field, as in example, it is 100211 and this is 10:02:11 UTC time.
greetings
You can use this:
#echo off
for /f "tokens=2 delims=,." %%a in ('type COM2 ^| find "GPRMC"') do (
set "comTime=%%a"
goto :break
)
:break
set "comTime=%comTime:~0,2%:%comTime:~2,2%:%comTime:~4,2%
echo %comTime%
pause
This sets the time of the first line in the file to the variable comTime in the format hh:mm:ss
NOTE
This removes the .279 part of the time, to keep that part change delims=,. to delims=,
Next code snippet would take line(s) from an endless command output.
Avoid | pipe to find or findstr (read more, see Pipes and CMD.exe paragraph).
Think about delay(s) caused by …
… involved timeout commands,
… handling temporary file and
… process handling (cmd, taskkill, for).
Tested using endless ping localhost -4 -t:
#ECHO OFF
SETLOCAL EnableExtensions DisableDelayedExpansion
set "mycommand=type COM2"
set "mytestval=$GPRMC"
rem next two lines are merely for my tests: remove them both
set "mycommand=ping localhost -4 -t"
set "mytestval=Reply"
start "bubu36260096" cmd /C ^>"%temp%\aux36260096.txt" %mycommand%
>NUL timeout /T 3 /NOBREAK
>NUL taskkill /T /F /FI "IMAGENAME eq cmd.exe" /FI "WINDOWTITLE eq bubu36260096"
>NUL timeout /T 1 /NOBREAK
rem v--- skip as many lines as you need
for /f "usebackq skip=1 tokens=1,2 delims=, " %%a in ("%temp%\aux36260096.txt") do (
rem ^--- remove this blank space in above line
if "%%a"=="%mytestval%" (
set "comTime=%%b"
goto :break
)
)
:break
rem next line for debugging purposes only
type "%temp%\aux36260096.txt"
rem set "comTime=%comTime:~0,2%:%comTime:~2,2%:%comTime:~4,2%
echo comTime ... "%comTime%"
pause
I am trying to make a text-based game, and when the user enters something, I want the computer's character to have some kind of thinking animation.
I've come up with this: [. ] [.. ] [...]
Only problem is, I want it all to be on one line, so it's like an actual animation. I've successfully recreated this without removed the text already echo'd, but it requires that you cls, then send all that was on the screen before, three times. Obviously this takes too much space and is pretty inefficient.
I found a code that changes the last like without clearing all that was before it,
#echo off
echo This won't disappear!
setlocal EnableDelayedExpansion
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
for /L %%n in (5 -1 1) do (
<nul set /P "=This window will close in %%n seconds!CR!"
ping -n 2 localhost > nul
)
It counts down without erasing the echo before, but I do not understand how to adapt it to add periods to my string... Does anyone know a way to do this? Thanks!
You were almost there with the code you found; you just need to use your own text instead of somebody else's.
for /L %%A in (1,1,3) do (
set /p "=."<nul
ping -n 2 localhost >nul
)
If you want to put the periods inside of brackets, you can use this:
for /f %%A in ('"prompt $H &echo on &for %%B in (1) do rem"') do set BS=%%A
set /p "=[ ]"<nul
ping -n 2 localhost >nul
set /p "=%BS%%BS%%BS%%BS%. ]"<nul
ping -n 2 localhost >nul
set /p "=%BS%%BS%%BS%. ]"<nul
ping -n 2 localhost >nul
set /p "=%BS%%BS%.]"<nul
ping -n 2 localhost >nul