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.
Related
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
)
I'm writing a batch to kill several processes if they are running and subsequently delete the files whether they were running or not. I'm very new to this so please bear with the horrible script.
#echo off
echo deleting files...
tasklist /FI "IMAGENAME eq file1.exe" | find /I /N "file1.exe"> nul 2>&1
if errorlevel 1 (
echo file1.exe is not running
goto :delete
) else (
taskkill /F /IM file1.exe> nul 2>&1
if errorlevel 1 (
echo Killing process file1.exe... Failed!
) else (
goto :delete
)
)
:delete
ren c:\file1.exe c:\tmp.tmp> nul 2>&1
if errorlevel 1 (
echo Deleting file1.exe... Access Denied!
) else (
del /q /f "C:\tmp.tmp"> nul 2>&1
echo Deleting file1.exe... Success!
)
rather than doing this for all 8 files, i'd like to use two lists for filenames and filepaths and run them both through a for loop at the same time. unfortunately i can't find much info online on how to loop through 2 lists in tandem and the following script would run into obvious problems:
for %%f in (file1 file2 file3) do
for %%p in (path1 path2 path3) do (
taskkill %f etc
del %p etc
)
some help would be appreciated!
You only need a single FOR loop with the full paths, and then use the %~nx modifiers to get the name only.
Here is the pseudo code:
for %%F in ("path1\file1" "path2\file2" "path3\file3") do (
taskkill %%~nxF
del %%F
)
For more information about the modifiers, use help for or for /? from the command line, and look toward the bottom of the help.
But there are times when you want to process lists of paired values. This can be done easily enough with two loops. The first loop iterates delimited pairs, and the 2nd FOR /F loop parses out each value from the pair. You just need to choose a delimiter that never appears in a value.
Using space delimiters:
for %%A in ("val1a val1b" "val2a val2b" "val3a val3b") do (
for /f "tokens=1,2" %%B in ("%%~A") do (
echo value1 = %%B
echo value2 = %%C
echo(
)
)
Using pipe delimiters: Note how you can span multiple lines for improved readability
for %%A in (
"val 1a|val 1b"
"val 2a|val 2b"
"val 3a|val 3b"
) do for /f "delims=| tokens=1,2" %%B in ("%%~A") do (
echo value1 = %%B
echo value2 = %%C
echo(
)
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.
I want to make a batch file that sends certain values to a output.txt. I have the following code:
for /F "delims=" %%a in ('adb logcat -v threadtime | grep -m 1 'VAC: App Name is:'') do set appName=%%a
echo App Name= %appName% > output.txt
The problem is that the grep statement doesn't immediately return a value--not until a user performs a certain action on the Android device. So I need to create a loop around the "for" statement. I'll make an attempt below, but I know it's wrong.
do
while
%appName% = ""
for /F "delims=" %%a in ('adb logcat -v threadtime | grep -m 1 'VAC: App Name is:'') do set appName=%%a
if "%appName% != "" leave
enddo
echo App Name= %appName% > output.txt
set "appName="
:loop
for /F "delims=" %%a in (
'adb logcat -v threadtime ^| grep -m 1 "VAC: App Name is:"'
) do set "appName=%%a"
if not defined appName goto loop
It just clears the variable and keep looping until it gets any value in it
Here you go (although MC ND's answers looks good too):
Please note pipe needs to be escaped by caret^, I added a 1 secs wait ping -n 1 -w 1000 1.1.1.1 >nul
:while_loop
for /F "usebackq delims=" %%a in (`adb logcat -v threadtime ^| grep -m 1 'VAC: App Name is:'`) do set appName=%%a
ping -n 1 -w 1000 1.1.1.1 >nul
if not "%appName% == "" goto exit_loop
goto while_loop
:exit_loop
echo App Name= %appName% > output.txt
This script tries to:
get an input from a txt file, which is a list of computer names,
check if a log file on a computer from the list is bigger than 1000 bytes,
create a txt report with the names of those computers where the log file is more than 1000 bytes,
create another txt report with the names of the computers where the file is less than 1000 bytes.
However, something goes wrong. Any help could be nice.
#echo off
for /f "tokens=*" %%I in (computernames.txt)do call :checksz %%I
goto :eof
:checksz
setlocal
set file="\\%1\c$\data\info.log"
set min=1000
FOR /F "usebackq" %%A IN ('%file%') DO set size=%%~zA
if %size% GTQ %min% (
echo. %1 >>logsizemin.txt
) ELSE (
echo. %1>>logsizemax.txt
)
Hello everyone,
thanks for your valuable support. I congratulate whith those who conceived and built this site is really well done and useful.
I made some modifications to the script that you have kindly suggested to incorporate other features, but something is not working as I would like .. as you can view I use editv32 in order to hide password in my batch, the principle is the same but as you can see after checking the size of the log, "maxlongsize.txt" is used in order to take the names of the PCs on which do the next activity. I wish that the activities were performed sequentially on all PCs in the file "logsizemax.txt" with only one authentication at the beginning. I noticed that, for some reason sometimes the file "logsizemin.txt" is not created but i don't understand why. The maximum would be to put in another file such as "computer unreachable" those PCs that are not reached on the network but I have absolutely no idea how implement it. I hope I have sufficiently explained. Sorry for bad English! I think you understand my intention :). Following the batch
#setlocal
#echo off
editv32 -p "Enter username:" USR
editv32 -m -p "Enter password:" PWD
for /f "tokens=1" %%I in (computernames.txt) do call :checksz %%I
endlocal
goto :eof
:checksz
setlocal
set file="\\%1\c$\data\data.log"
set min=1000
type NUL>logsizemax.txt
type NUL>logsizemin.txt
if not exist %file% goto :eof
FOR /F "usebackq delims=" %%A IN ('%file%') DO set size=%%~zA
if %size% GEQ %min% ( echo %1>>logsizemax.txt ) ELSE ( echo %1>>logsizemin.txt )
endlocal
#setlocal
for /f "tokens=1" %%I in (logsizemax.txt) do call :sw %%I
endlocal
goto :eof
:sw
echo.
echo *****************************************
echo * VBS & Deploy script *
echo *****************************************
echo.
echo Run VBS and deploy script .....
psexec \\%1 -u %USR% -p %PWD% cscript "\\rep\Reg.vbs"
psexec \\%1 -u %USR% -p %PWD% cmd /c "\\rep\deploy.cmd"
echo.
echo **************************
echo * EXE Run *
echo **************************
echo.
echo Running exe .....
psexec -u %USR% -p %PWD% \\%1 "c:\Program Files\test.exe"
echo.
echo ***********************************
echo * Exe application launched *
echo ***********************************
echo.
goto END
:END
exit
You can avoid using environment variables and using the second FOR alltogether. Try this simpler version of your bat, with a more generic :checksz routine.
#echo off
for /f "tokens=*" %%a in (computernames.txt) do (
call :checksz "\\%%a\c$\data\info.log" 1000 "%%a"
)
goto :eof
:checksz
if %~z1 GTR %2 (
echo %~3 >> logsizemin.txt
) ELSE (
echo %~3 >> logsizemax.txt
)
goto :eof
see HELP CALL for more information.
Changes: GTG->GEQ, don't surround variable with quotes twice, remove leading space from echo, and a little clean up.
#setlocal
#echo off
for /f "tokens=1" %%I in (computernames.txt) do call :checksz %%I
endlocal
goto :eof
:checksz
setlocal
set file=\\%1\c$\data\info.log
set min=1000
if not exist %file% endlocal && goto :eof
FOR /F "usebackq delims=" %%A IN ('%file%') DO set size=%%~zA
if %size% GEQ %min% ( echo %1>>logsizemin.txt ) ELSE ( echo %1>>logsizemax.txt )
endlocal
goto :eof
edit: updated per comments from PA and Andriy M - endlocal if the file doesn't exist, and remove \ update note.