Batch Script ErrorLevel Output - batch-file

I need help - I am trying to output any ErrorLevel that is 1 or greater into a log file. When I issue the command the log file never get's generated. Any help would be greatly appreciated.
Script:
for /f "delims=" %%i in (C:\_\Restart\Computer.txt) do (
start "%%i" \_\PStools\psexec \\%%i -u Administrator -p Password -i c:\restart.cmd
if not %errorlevel%==0 echo %errorlevel% > error.log
)
This script allows me to use PSEXEC and issue a restart command to all the computer at once. However several of them fail and I'd like to know which ones fail.
Thanks!
Is this the format I should use?
setlocal EnableDelayedExpansion
for /f "delims=" %%i in (C:\_\Restart\Computer.txt) do (
start "%%i" \_\PStools\psexec \\%%i -u Administrator -p Password -i c:\restart.cmd
if errorlevel 1 echo !errorlevel! > error.log
)

Script V3:
setlocal EnableDelayedExpansion
for /f "delims=" %%i in (C:\temp\list.txt) do (
start "" "shutdown" /m \\%%i -r -f -t 900
echo !errorlevel! && echo %%i
if errorlevel 1 echo !errorlevel! >> c:\temp\log.txt && echo %%i >> c:\temp\log.txt
)
-m = use remote computer
-r = reboot
-f = Force reboot
-t = delay of time before rebooting
you can use shutdown -? for more help on argument that can be passed to the reboot command.
Script v4 without the start command:
setlocal EnableDelayedExpansion
for /f "delims=" %%i in (C:\temp\list.txt) do (
shutdown /m \\%%i -r -f -t 900
echo !errorlevel! && echo %%i
if errorlevel 1 echo !errorlevel! >> c:\temp\log.txt && echo %%i >> c:\temp\log.txt
)

Related

Find all .sql-Files in a Directory and execute them with sqlcmd

I'm still a CMD beginner and would like to import all SQL files I have in a directory structure with sqlcmd to my DB.
If I copy all *.sql into the root folder the following command works:
#ECHO ON
FOR %%G in (*.sql) DO sqlcmd /S *SERVER* /d *DB* /U *USER* /P *PW* -i"%%G" > LOG_lastrun.txt 2> LOG_errors.txt
pause
#ECHO ON
FOR /F "tokens=1 delims=sql" %%G in (*.sql) DO sqlcmd /S *SERVER* /d *DB* /U *USER* /P *PW* -i"%%G" > LOG_lastrun.txt 2> LOG_errors.txt
pause
Unfortunately, it doesn't work for me FOR /F loops. Can you help me here?
The FOR /F works differently. It runs a command. Jusr *.sql is not a command. Also, I suspect that you want to concatenate stdout and stderr output using >>.
#ECHO ON
SET "SERVER_NAME=*SERVER*"
SET "DB_NAME=*DB*"
SET "LOGIN_ID=*USER*"
SET "PASS=*PW*"
FOR /F "delims=" %%G IN ('DIR /B "*.sql"') DO (
sqlcmd -S %SERVER_NAME% -d %DB_NAME% -U %LOGIN_ID% -P %PASS% -i "%%~G" >> LOG_lastrun.txt 2>> LOG_errors.txt
)
pause

Pass current file name from loop to label in batch script

In a batch script I'm running sqlcmd in a loop that goes through a folder of SQL scripts. I would like to be able to pass the script/output file currently being worked on to a label exterior to the loop in the case of an error.
Here's sample code:
#echo off
#setlocal enabledelayedexpansion
SET _INSTANCE=someinstance
SET _DATABASE=somedatabase
SET "_SCRIPTFOLDER=D:\Scripts for Testing"
SET "_OUTPUTFOLDER=D:\Output for Testing"
FOR %%S IN (
"%_SCRIPTFOLDER%\*.sql"
) DO (
SET /P _MSGa=Generating CSV: %%~nS.csv ... <NUL
sqlcmd -b -S %_INSTANCE% -d %_DATABASE% -i "%%~fS" -s "|" -o "%_OUTPUTFOLDER%\%%~nS.csv" -W
IF ERRORLEVEL >= 1 GOTO sqlcomderrorhandling
SET /P _MSGb=file created. Removing header dashes ... <NUL
REM REM Remove the line with dashes below the header
#FINDSTR /r /b /v /c:"-*|" "%_OUTPUTFOLDER%\%%~nS.csv" > "%_OUTPUTFOLDER%"\tmp.txt
IF ERRORLEVEL >= 1 GOTO findstrerrorhandling
XCOPY /Y "%_OUTPUTFOLDER%"\tmp.txt "%_OUTPUTFOLDER%\%%~nS.csv" >NUL
IF ERRORLEVEL >= 1 GOTO copyerrorhandling
ECHO done.
)
DEL /Q /F "%_OUTPUTFOLDER%"\tmp.txt
GOTO done
:sqlcomderrorhandling
ECHO An error occurred while processing the file %%~nS.csv
:done
#pause
The last ECHO just outputs %~nS.csv, not the actual name of the CSV file. Do I need to utilize functions in some way to do what I want to do?
Based on part of rojo's first comment and my own, would this sort of structure not make more sense?
#Echo Off
Set "_INSTANCE=someinstance"
Set "_DATABASE=somedatabase"
Set "_SCRIPTFOLDER=D:\Scripts for Testing"
Set "_OUTPUTFOLDER=D:\Output for Testing"
If Not Exist "%_OUTPUTFOLDER%\" (Echo Output folder doesn't exist
Timeout 3 /NoBreak >Nul
GoTo :EOF)
If Not Exist "%_SCRIPTFOLDER%\*.sql" (Echo Source folder doesn't contain any SQL files
Timeout 3 /NoBreak >Nul
GoTo :EOF)
CD /D "%_SCRIPTFOLDER%" 2>Nul || (Echo Source folder, %_SCRIPTFOLDER%, is not available.
Timeout 3 /NoBreak >Nul
GoTo :EOF)
For %%A In (*.sql) Do (
Echo Generating CSV: %%~nA.csv ...
SQLCmd -b -S %_INSTANCE% -d %_DATABASE% -i "%%A" -s "|" -o "%_OUTPUTFOLDER%\%%~nA.csv" -W 2>Nul && (
Echo File created. Removing header dashes ...
Findstr "[^-|]" "%_OUTPUTFOLDER%\%%~nA.csv">"%_OUTPUTFOLDER%"\%%~nA.tmp" && (
Move /Y "%_OUTPUTFOLDER%"\%%~nA.tmp" "%_OUTPUTFOLDER%\%%~nA.csv">Nul 2>&1 || (
Echo A Move error occurred while moving %%~nA.tmp
Timeout 2 /NoBreak >Nul)) || (Echo A FindStr error occurred while removing header dashes
Timeout 2 /NoBreak >Nul)) || (Echo A SQLCmd error occurred while processing the file %%~nS.csv
Timeout 2 /NoBreak >Nul))
Pause

Batch file loop issue - ends batch file

I have problem with my batch file.
#echo off
:START
echo Are you ready?
SET logpath=xxx
:COPY
pause
xxx
:FIRST
for /f %%g IN (hostnames.txt) do (
::xcopy "xxx*.*" \\%%g\c$\temp\ /f /s /y /i
echo PC %%g >> %logpath%\xxx
echo %%g > appname.txt
psexec -h \\%%g cmd /c msiexec /i xxx
IF '%ERRORLEVEL%'=='0' (
echo xxx Success! >> %logpath%\xxx.txt
) else (
echo xxx Error is %ERRORLEVEL%. >> %logpath%\xxx.txt
)
psexec -h \\%%g cmd /c msiexec /i xxx
IF '%ERRORLEVEL%'=='0' (
echo xxx Success! >> %logpath%\xxx.txt
) else (
echo xxx Error is %ERRORLEVEL%. >> %logpath%\xxx.txt
)
:SECOND
psexec -h \\%%g cmd /c MsiExec.exe /I{xxx} /passive /norestart
IF '%ERRORLEVEL%'=='0' (
echo test {...-xxx} uninstalled! >> %logpath%\xxx.txt
goto THIRD
)
psexec -h \\%%g cmd /c msiexec /x "{xxx}" /passive /norestart
IF '%ERRORLEVEL%'=='0' (
echo Pierwszy {...-xxx} uninstalled! >> %logpath%\xxx.txt
goto THIRD
)
::Like 15 times uninstalling different apps
:THIRD
for /f %%g in (appname.txt) do taskkill -s %%g -im xxx.exe -f
for /f %%g in (appname.txt) do psexec -h \\%%g cmd /c msiexec /i xxx /passive /norestart
IF '%ERRORLEVEL%'=='0' (
echo xxx Success! >> %logpath%\xxx.txt
) else (
echo xxx. Error is %ERRORLEVEL%. >> %logpath%\xxx.txt
)
for /f %%g in (appname.txt) do psexec -h \\%%g cmd /c regedit /s xxx.reg
for /f %%g in (appname.txt) do psexec -h \\%%g cmd /c del c:\temp\xxx\*.* /f /s /q
echo. >> %logpath%\xxx.txt
echo. >> %logpath%\xxx.txt
)
At first, like You can see in the THIRD section I need to use for /f %%g in (appname.txt) do psexec -h instead of psexec -h, because psexec fails and it's "losing" hostname and this is the only workaround which I have found by myself.
Second thing (most important) is that when batch runs to the end (to the last ")") it stops. Like the first loop for was ended and it doesn't get new hostname from file hostnames.txt
I would be gratefull for any ideas how to make it work

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.

Batch File - Catching PSEXEC result

If I run a successful PSEXEC command, it says this...
"cmd exited on workstation.domain with error code 0."
Is there any way I can prevent this and do something like
psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "
if %errorlevel%==0 (
echo Success!
) else (
REM display psexec error here.
)
Since my edit's were rejected...
Is the original code posted part of a larger script? If so then do you set your errcode to match the ERRORLEVEL environment variable?
psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "
IF '%ERRORLEVEL%'=='0' (
echo Success!
) else (
REM display psexec error here.
)
Whenever attempting to detemine IF / THEN in batch and you use == you need to surrond the variable and the valuecheck in single " ' " marks. The above code corrects that issue for you as well as replaces errcode with ERRORLEVEL, which is the default environment variable for Windows.
Also, in practice I always use the following before any ERRORLEVEL check to drop the initial value to properly catch the error.
verify >nul
In this case I would do the following:
verify >nul
psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "
IF '%ERRORLEVEL%'=='0' (
echo Success!
) else (
echo.Error is %ERRORLEVEL%; please see http://msdn.microsoft.com/en-us/library/ms681381(VS.85).aspx for more details.
)
I added a weburl for checking on the error received.
Alternatively you could open the URL to the corresponding page automatically:
#ECHO OFF
verify >nul
set ERRCODE=0
psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "
IF '%ERRORLEVEL%'=='0' (
echo Success!
) else (
set ERRCODE=%ERRORLEVEL%
)
IF %ERRCODE% LEQ 499 set MSERROR=681382
IF %ERRCODE% GTR 500 set MSERROR=681388
IF %ERRCODE% GTR 1000 set MSERROR=681383
IF %ERRCODE% GTR 1300 set MSERROR=681385
IF %ERRCODE% GTR 1700 set MSERROR=681386
IF %ERRCODE% GTR 4000 set MSERROR=681387
IF %ERRCODE% GTR 6000 set MSERROR=681389
IF %ERRCODE% GTR 8200 set MSERROR=681390
IF %ERRCODE% GTR 9000 set MSERROR=681391
IF %ERRCODE% GTR 12000 set MSERROR=681384
IF ERRCODE NEQ 0 start http://msdn.microsoft.com/en-us/library/ms%MSERROR%(v=vs.85).aspx
IF ERRCODE NEQ 0 echo.This failed with ERROR: %ERRCODE%
pause
For reference to psexec http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx
The first step when determining program results is to identify all of the return values and if it sets errorlevel.
#echo off
:: Method 1, Handle a single line of output. No errorlevel support
for /f "usebackq delims=" %%A in (`psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= " ^| find /v "error code 0"`) do (
rem Display the error
echo.%%A
goto Failed
)
echo.Success
:Failed
:: Method 2, Handle multiple lines of output. No errorlevel support
for /f "usebackq delims=" %%A in (`psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "`) do (
rem Check the status
for /f "usebackq delims=" %%X in (`echo."%%~A" ^| find /v "error code 0"`) do (
echo.%%X
)
for /f "usebackq delims=" %%X in (`echo."%%~A" ^| find "error code 0"`) do (
echo.Success
)
)
:: Method 3, Supports error level variable; only works if the called program supports it.
verify > nul
psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "> nul
if %ERRORLEVEL% EQU 0 echo.Success
if %ERRORLEVEL% NEQ 0 echo.Error
:: Method 4, specific error message with error level, requires delayed expansion.
setlocal enabledelayedexpansion
verify > nul
for /f "usebackq delims=" %%A in (`psexec \\workstation.domain -u username -p password cmd /c "assoc.pdf= "`) do (
if !ERRORLEVEL! EQU 0 echo.Success
if !ERRORLEVEL! NEQ 0 echo.%%A
)
endlocal
pause

Resources