skip a computers in a batch file that is unreachable - batch-file

when the script is running, when it finds a machine to be unreachable I want it to skip it. and do psexec command for the online PCs only
i created a bat file as below but it's not working
#echo off
setlocal
:begin
for /f %%a in (computerlist.txt) do (
ping -n 1 %%a >NUL 2>NUL
if %errorlevel%==0 (
psexec \\%%a -u user -p password -i -d "d:\path\command.exe"
) else echo Skipping unreachable host %%a
)
endlocal

wmic /node:"#computerlist.txt" /failfast:on process call create "c:\\windows\\notepad.exe"
It doesn't wait for computers that don't answer quickly. Notepad will be invisible on remote machine but not a local machine.

You need to add Setlocal EnableDelayedExpansion to your code and access the errorlevel as !errorlevel!:
#echo off
Setlocal EnableDelayedExpansion
for /f %%a in (computerlist.txt) do (
ping -n 1 %%a >NUL 2>NUL
if !errorlevel!==0 (
psexec \\%%a -u user -p password -i -d "d:\path\command.exe"
) else echo Skipping unreachable host %%a
)
The problem is that usually variables are being evaluated at parse time. So if you use %errorlevel% it will always contain the first set value. EnableDelayedExpansion will force evaluation at execution time instead so !errorlevel! will always contain the last assigned value (while %errorlevel% will still contain the first one). In your case the variable might change on each iteration of the loop so you obviously need !errorlevel!.
For more information check http://ss64.com/nt/delayedexpansion.html.

Related

Batch: How to store command output with spaces in variable?

I want to make a Batch file to create windows firewall rules for an executable (for example, python). To do this, I have the following script:
REM ~ Open ports. Run with elevated administrator rights.
CALL :OPEN "python"
GOTO :EOF
:OPEN
IF ERRORLEVEL 1 (
GOTO :EOF
)
FOR /F %%i IN ('where %1') DO CALL :OPEN_PROGRAM "%1" %%i && GOTO :EOF
GOTO :EOF
:OPEN_PROGRAM
FOR %%j IN (TCP UDP) DO CALL :OPEN_PROGRAM_PORT %1 %2 "%%j"
GOTO :EOF
:OPEN_PROGRAM_PORT
netsh advfirewall firewall add rule name=%1 description=%1 dir=in action=allow edge=deferuser profile=private program=%2 protocol=%3
GOTO :EOF
This works fine as long as there are no spaces in the path to the executable.
If there are spaces in the path, then only the part up to the spaces is put in the %%i variable. I tried using 'where /F %1' , but then it still cuts the output on the space.
Is there a way to store the full output of where %1 in a variable? (Preferably without writing it to a file)
I would first advise you not to pass an filename without an extension. Doing so relies upon each of the extensions listed under %PATHEXT%, and you could have files named python.com, python.bat, python.cmd, python.vbs, python.vbe, python.js, python.jse, python.wsf, python.wsh, and python.msc, as well as python.exe. I'm sure you wouldn't want to unknowingly create firewall rules for all of those.
I would also advise that you do not use the where command for this. The reason being that it could return more than a single item. For example, try this, where notepad.exe and if memory serves, you'll probably see, C:\WINDOWS\System32\notepad.exe, followed by C:\WINDOWS\notepad.exe. That is because where.exe searches each location in %PATH% in order of their listing, and returns each, as the first two entries under %PATH% should by default be C:\WINDOWS\system32;C:\WINDOWS;.
My suggestion therefore would be to use another method, which returns the first item found under %PATH% as opposed to all, and this should, if you've used the variable properly, return the default, (most used) item. The method is the %~$PATH variable expansion, and is documented under the help information for the for command, i.e. entering for /? at the Command Prompt.
Example Script:
#Echo Off
SetLocal EnableExtensions
Rem ~ Open ports. Run with elevated administrator rights.
"%__AppDir__%reg.exe" Query HKU\S-1-5-19 1>NUL 2>&1 || (
Echo This script must be run elevated.
Echo Please use 'Run as administrator'
"%__AppDir__%timeout.exe" /T 3 /NoBreak 1>NUL
GoTo :EOF)
Call :OpenPorts "python.exe"
Pause
GoTo :EOF
:OpenPorts
For %%G In ("%~1") Do For %%H In (TCP UDP) Do (
"%__AppDir__%netsh.exe" AdvFirewall Firewall Add Rule^
Name="%~n1" Description="%~1" Dir=in Action=allow^
Edge=deferuser Profile=private Program="%%~$Path:G"^
Protocol=%%H)
Exit /B
I have removed all of the unnecessary Call commands, (using just the one), and deliberately used carets to split up your super long line, for readability; (which also allows you to include the localport= and/or remoteport= option, which I'd assume by your 'Open Ports' name you're going to want to include as a modification to the above later). I also took the liberty of including some code, to determine if the script was being run elevated, and display a message before closing, if it isn't.
Write the output of where.exe to a file, then read the file one line at a time. There is a way to do it without a temp file after the "===" line.
#ECHO OFF
SET "XTOFIND=%~1"
SET "TEMPFILE=%TEMP%\wherelist.tmp"
IF EXIST "%TEMPFILE%" (DEL "%TEMPFILE%")
"%__appdir__%where.exe" "%XTOFIND%" >"%TEMPFILE%"
FOR /F "tokens=*" %%A IN ('TYPE "%TEMPFILE%"') DO (
ECHO CALL :OPEN_PROGRAM "%%~A" %2 "%%J"
)
IF EXIST "%TEMPFILE%" (DEL "%TEMPFILE%")
ECHO ============
SET "XTOFIND=%~1"
FOR /F "tokens=*" %%A IN ('"%__appdir__%where.exe" "%XTOFIND%"') DO (
ECHO %%~A
ECHO CALL :OPEN_PROGRAM "%%~A" %2 "%%J"
)
EXIT /B 0

How to remove trailing space in output file

This is my first post to this site, though I use it quite often. I wrote a batch file to do the following, ideally. Note: There are alot of echos in here, to help me debug.
1.) Clean up old log files, this works file
2.) Ping a list of systems and output successful results to one file and bad to another
3.) Check if software exists, if not download it
4.) Install software on endpoint
The issue I am encountering is the successful ping output files has trailing spaces. Because of this, the install commands don't work. It puts the trailing space in the UNC path. Since the spaces exist, the run command doesn't work.
HELP PLEASE, My desk is breaking, due to my forehead smashing into it.
#echo off
cls
set file=_0_Add_Systems_Here.txt
set log1=_3_Responsive.txt
set log2=_4_Non-Responsive.txt
set dir=%cd%
FOR /F "usebackq" %%i IN (`hostname`) DO SET host=%%i
echo Run Cleanup
start _2_Dont_Run_Me.bat
echo Ping
for /F "tokens=*" %%a in (%file%) do #ping %%a | find "TTL=" > nul && echo %%a >> %log1% || echo %%a >> %log2%
echo Output systems
for /F "tokens=*" %%a in (%log1%) do echo "%%a"
echo Check for Framepkg
if not exist framepkg.exe Copy \\<Removed>\c$\MFE\framepkg.exe | echo copying frame package.
if exist framepkg.exe echo Found frame package.
echo copy framepackage to system
for /F "tokens=*" %%b in (%log1%) do copy %dir%\framepkg.exe \\%%b\c$\framepkg.exe /y >> _5_McAfee_Deployment_Output.txt
for /F "tokens=*" %%b in (%log1%) do echo \\%%b\c$\framepkg.exe
echo start agent install
for /F "tokens=*" %%c in (%log1%) do psexec \\%%c cmd /c "c:\framepkg.exe /install=agent /forceinstall" >> \\%host%\%dir%\_5_McAfee_Deployment_Output.txt
....&& >>%log1% echo %%a||>>%log2% echo %%a
should fix your problem (the extra spaces in your code appear to cause the problem; the redirector location within the statement is not relevant - first is as good as last)
OR
....&& echo %%a\ >> %log1% || echo %%a\ >> %log2%
AND
for /F "delims=\" %%b in (%log1%)....
but that would place extra \ in your log files.
(reason for \ is that it can't exist in valid data)

parallel processing in bat file for loop

I want a batch file to do parallel processing. I have a storedprocedure which returns 1000+ records(has unique rowid column along with other info). Iterating though each row works fine. However, it takes long time to complete 1000 loops. Is there a way to run two loops parallel without overlapping or having to maintain separate batch files. Can this be accomplished by having one .bat file.
WORKING CODE:
#echo off
setlocal EnableExtensions
set ListFile=%TEMP%\StudentList.tmp
Set varServerPath=http://xyz/ReportServer
sqlcmd -Q "exec dbo.Storedproc_StudentList" -S ServerName -d DatabaseName >"%ListFile%" 2>nul
if exist "%ListFile%" (
for /F "usebackq tokens=1,2,3,4 skip=2 delims=', " %%A in ("%ListFile%") do (
echo Processing StudentID %%A and SubjectID %%B ...
if not exist "%%D" mkdir "%%D"
rs -i C:\ReportRender\Student.rss -s%varServerPath% -e Exec2005 -v StudentID="%%A" -v SubjectID="%%B" -v vOutputFilePath="%%C" -v vReportPath="/Student Reports/ReportName.rdl" -l 900
)
del "%ListFile%"
)
exit
I tried doing something like having two for loops one from 1 to 200 and other from 201 to 400 and so on....but seems like i'm on the wrong track. It doesn't work, Please suggest.
#echo off
setlocal EnableExtensions
set ListFile=%TEMP%\StudentList.tmp
Set varServerPath=http://xyz/ReportServer
sqlcmd -Q "exec dbo.Storedproc_StudentList" -S ServerName -d DatabaseName >"%ListFile%" 2>nul
if exist "%ListFile%" (
for /F "usebackq tokens=1,2,3,4 skip=2 delims=', " %%A in ("%ListFile%") do (
for /L %%A in(1,1,200) do (
echo Processing StudentID %%A and SubjectID %%B ...
if not exist "%%D" mkdir "%%D"
rs -i C:\ReportRender\Student.rss -s%varServerPath% -e Exec2005 -v StudentID="%%A" -v SubjectID="%%B" -v vOutputFilePath="%%C" -v vReportPath="/Student Reports/ReportName.rdl" -l 900
)
for /L %%A in(201,1,400) do (
echo Processing StudentID %%A and SubjectID %%B ...
if not exist "%%D" mkdir "%%D"
rs -i C:\ReportRender\Student.rss -s%varServerPath% -e Exec2005 -v StudentID="%%A" -v SubjectID="%%B" -v vOutputFilePath="%%C" -v vReportPath="/Student Reports/ReportName.rdl" -l 900
)
)
del "%ListFile%"
)
exit
Thanks,
Your approach is wrong. You are executing a for /L %%A in (1,1,200) ... and a for /L %%A in (201,1,400) ... for each record in the %ListFile%. You need to distribute the records in the %ListFile% into the two parallel processes. Although this can be done in groups of 200 records, it is much simpler to do that one-by-one. Also, the only way to have parallel processes in a Batch file is via start command or using a | pipe. In this case you want to distribute several input records that will be read (and processed) by two "output processes", so the pipe approach is simpler.
#echo off
setlocal EnableDelayedExpansion
if "%1" neq "" goto %1
set ListFile=%TEMP%\StudentList.tmp
Set varServerPath=http://xyz/ReportServer
sqlcmd -Q "exec dbo.Storedproc_StudentList" -S ServerName -d DatabaseName >"%ListFile%" 2>nul
if not exist "%ListFile%" exit
set numProcs=2
( "%~F0" Input | "%~F0" Output ) 2>&1 | "%~F0" Output
del "%ListFile%"
exit
:Input
set i=0
for /F "usebackq tokens=1,2,3,4 skip=2 delims=', " %%A in ("%ListFile%") do (
set /A i+=1, proc=i%%numProcs
if !proc! equ 1 (
echo %%A %%B %%C %%D
) else (
>&2 echo %%A %%B %%C %%D
)
)
exit /B
:Output
for /F "tokens=1-4" %%A in ('findstr "^"') do (
echo Processing StudentID %%A and SubjectID %%B ...
if not exist "%%D" mkdir "%%D"
rs -i C:\ReportRender\Student.rss -s%varServerPath% -e Exec2005 -v StudentID="%%A" -v SubjectID="%%B" -v vOutputFilePath="%%C" -v vReportPath="/Student Reports/ReportName.rdl" -l 900
)
exit /B
The :Input part just distribute the "%ListFile%" records to Stdout (channel 1) and Stderr (channel 2), one by one.
The :Output part just take the %%A %%B %%C %%D values sent by :Input part and process they in the usual way; the input data is read from Stdin via findstr command.
The :Input and :Output parts could be in separate Batch files, but they are included in the same file and selected via a parameter and the if "%1" neq "" goto %1 command placed at beginning.
The most interesting code is the pipeline that run the 3 processes in parallel. The :Input part run and its Stdout output is feed into the first :Output process. The Stderr output (channel 2) of :Input part is redirected into Stdin via the 2>&1, so this output is feed into the second :Output process.
This method may also be used for more than two output parallel processes; you just need to add more similar parts changing the number 2 for 3, etc. For example, with three output processes the pipeline should be this one:
( ( "%~F0" Input | "%~F0" Output ) 2>&1 | "%~F0" Output ) 3>&1 | "%~F0" Output
However, it is very important that you note that this method does NOT necessarily imply that the whole process will run faster! This point depends on several factors, like the number of CPU cores and the speed/buffers of the disk drive. Just a test can answer this question...
Post the result, please.

Loop not looping in batch file

I have an issue and can't seem to see where I am going wrong.
I have this code, that is meant to loop through a text file, and look at the last word of the line to see if the user at the start of the line should receive elevated access to their assigned laptop.
However the code just runs through once, and then exits.
#echo off
cls
echo.
echo The following script will process the entire list of students, giving each
echo student administration rights to the laptop and set the laptop description
echo where necessary.
echo.
pause
cls
FOR /F "tokens=1-6 delims=," %%a IN (accesselevations.txt) DO (
set School1=%%a
set LaptopID=%%b
set Model1=%%c
set Serial1=%%d
set User1=%%e
set Access1=%%f
If /i "%%f" EQU "Admin" goto Elevate2
If /i "%%f" EQU "NoAdmin" goto NoElevate2
:Elevate2
set Desc1=!School1! - !LaptopID! - !Model1! - !Serial1! - !User1!
echo.
echo Now creating local admin account for !user! on !LaptopID!
echo.
echo Description: !Desc1!
echo.
pause
psexec \\!LaptopID! -u GBN\!ocusr! -p !ocpw! -n 10 -e net localgroup Administrators "GBN\!User1!" /add
psexec \\!LaptopID! -u GBN\!ocusr! -p !ocpw! -n 10 -e net config server /srvcomment:"!Desc1!"
:NoElevate2
cls
Echo.
Echo User !user1! is not allowed Local Administrator rights on !LaptopID!.
pause
)
pause
:End
The file AccessElevations has confidential data in it so I am not able to post it unfortunately, however it contains something like this
School,WorkstationID,LaptopModel,LaptopSerial,StudentUsername,AdminOrNot
School,WorkstationID,LaptopModel,LaptopSerial,StudentUsername,AdminOrNot
School,WorkstationID,LaptopModel,LaptopSerial,StudentUsername,AdminOrNot
School,WorkstationID,LaptopModel,LaptopSerial,StudentUsername,AdminOrNot
...and so-on.
I hope I have given enough information, I'm fairly new here.
Thanks in advance for any light you can spread on the issue.
Toby
Use setlocal enabledelayedexpansion or you won't able to use exclamation point on variables.
Close your FOR loop before Elevate2
Change this:
If /i "%%f" EQU "Admin" goto Elevate2
If /i "%%f" EQU "NoAdmin" goto NoElevate2
To:
If /i "%%f" EQU "Admin" (CALL:Elevate2) else (CALL:NoElevate2)
Using CALL command, your script will return to the end of FOR loop after elevation operation is done, GOTO command connot do this.
Put exit/b before :NoElevate2, otherwise your script won'r return to CALL command point.
Your code should be like this:
#echo off
setlocal enabledelayedexpansion
cls
echo.
echo The following script will process the entire list of students, giving each
echo student administration rights to the laptop and set the laptop description
echo where necessary.
echo.
pause
cls
FOR /F "tokens=1-6 delims=," %%a IN (accesselevations.txt) DO (
set School1=%%a
set LaptopID=%%b
set Model1=%%c
set Serial1=%%d
set User1=%%e
set Access1=%%f
If /i "%%f" EQU "Admin" (CALL:Elevate2) else (CALL:NoElevate2))
:Elevate2
set Desc1=!School1! - !LaptopID! - !Model1! - !Serial1! - !User1!
echo.
echo Now creating local admin account for !user! on !LaptopID!
echo.
echo Description: !Desc1!
echo.
pause
psexec \\!LaptopID! -u GBN\!ocusr! -p !ocpw! -n 10 -e net localgroup Administrators "GBN\!User1!" /add
psexec \\!LaptopID! -u GBN\!ocusr! -p !ocpw! -n 10 -e net config server /srvcomment:"!Desc1!"
exit/b
:NoElevate2
cls
Echo.
Echo User !user1! is not allowed Local Administrator rights on !LaptopID!.
pause
exit/b
:End
#echo off
setlocal
cls
echo.
echo The following script will process the entire list of students, giving each
echo student administration rights to the laptop and set the laptop description
echo where necessary.
echo.
pause
cls
FOR /F "tokens=1-6 delims=," %%a IN (accesselevations.txt) DO (
set School1=%%a
set LaptopID=%%b
set Model1=%%c
set Serial1=%%d
set User1=%%e
set Access1=%%f
If /i "%%f" EQU "Admin" CALL :Elevate2
If /i "%%f" EQU "NoAdmin" CALL :NoElevate2
)
GOTO :EOF
:Elevate2
set Desc1=%School1% - %LaptopID% - %Model1% - %Serial1% - %User1%
echo.
echo Now creating local admin account for %user1% on %LaptopID%
echo.
echo Description: %Desc1%
echo.
ECHO psexec \\%LaptopID% -u GBN\%ocusr% -p %ocpw% -n 10 -e net localgroup Administrators "GBN\%User1%" /add
ECHO psexec \\%LaptopID% -u GBN\%ocusr% -p %ocpw% -n 10 -e net config server /srvcomment:"%Desc1%"
GOTO :eof
:NoElevate2
Echo.
Echo User %user1% is not allowed Local Administrator rights on %LaptopID%.
GOTO :EOF
Notes:
setlocal added to ensure environment does not get polluted by a run
Doesn't appear to be much point in setting Access1 since it isn't used.
CALL :routine rather than goto - Not a ood idea to goto within a block as some cmd versions appear to work differently. Colon is required for running internal subroutine
Within internal subroutines, can use %var% since these run in their own context.
!var! syntax is only effective when invoked by setlocal enabledelayedexpansion (not executed in original batch.)
psexec commands simply ECHOed. Need to remove the preceding echo to execute the psexec
Shouldn't a 'remove privileges' routine be applied for 'Nodamin' ?
!user! changed to %user1% in :elevate2 since user1 is established by the loop, not user.
Suitable datafile used for testing was:
School1,Workstation1ID,Laptop1Model,Laptop1Serial,Student1Username,Admin
School2,Workstation2ID,Laptop2Model,Laptop2Serial,Student2Username,NoAdmin
School3,Workstation3ID,Laptop3Model,Laptop3Serial,Student3Username,Admin
School4,Workstation4ID,Laptop4Model,Laptop4Serial,Student4Username,NoAdmin
not that hard to construct - could easily be done with a prettier version by substituting for names and other sensitive information.

How to know the output of a previous command in a batch script

#echo off
setlocal enabledelayedexpansion
>result.txt (
for /f %%a in (srvrname.txt) do (
ping -n 1 %%a > nul && echo %%a up||echo %%a DOWN
))
I was doing a ping test for some servers with the above script. I want to understand how the output of the ping command is passed to echo UP or echo DOWN section..As in unix we have $? to know if the previous command executed successfully like if[$? -eq 0] then; do success else failure done.
How does this happen in a batch script. DO WE HAVE A VARIABLE like $? in batch. Please let me know. Also please suggest if we could make the pingtest script any better.
I think you are looking for %errorlevel%.
This will give you the error code for the last command that executed.
if %errorlevel%==0 echo SUCCESS
if %errorlevel%==1 echo FAIL
Note: There is a whole range of errorcodes that could be set, I just used 1 as an example, check the command documentation to find the errorcode's meaning.
Beware though, if you are using %errorlevel% in your for loop you will need to use delayed expansion.
You already have it added on your script (setlocal enabledelayedexpansion), so you just need to change the %'s to !'s.
#echo off
setlocal enabledelayedexpansion
>result.txt (
for /f %%a in (srvrname.txt) do (
ping -n 1 %%a > nul
if !errorlevel!==0 (
echo Ping replied successfully
) else (
echo There was an error
)
))

Resources