Jump to content

Batch script for windows & software updates installation


Recommended Posts

@crobertson

 

I guess you are replying to me?

 

I have looked at your requests and created a script based on those requests. It saves the installed hotfixes read from systeminfo\wmi to a file and creates a updates text file with the msu files and their installed state. It has a limit variable so if you have say 200 msu files and want to install in 3 separate executions, then you could set the variable to 70. It would then stop at 70 installs. The next execution of the script would do the next 70 installs. The updates text file will be updated with the istalled status at each script execution.

 

The execution of systeminfo\wmi is done just once so later script executions should perform faster based solely on the updates text file. Using variable substitution instead of using find.exe speeds up the script as well.

@echo offsetlocal enabledelayedexpansion:: Note: Remove the ECHO in front of the wusa keyword to enable installations.:: The delimiters in the saved files are tabs.:: Update the values of the next 4 set variables if needed.:::: The filepath used to save the KB information.set "systeminfo=%~dp0systeminfo.txt":::: The filepath used to save the msu filepaths and the installed status.set "updates=%~dp0updates.txt":::: Limit number of KB hotfix installs during current script execution.set /a "limit=9999":::: Use wmi instead of systeminfo. Use 0 for systeminfo.exe.set "wmi=1"if not exist "%updates%" (	rem Find installed KB numbers from wmi\systeminfo and save to file.	if "%wmi%" == "0" (		for /f "tokens=2" %%A in ('systeminfo^|findstr /i "\<KB[0-9][0-9]*$"') do (			(echo %%A) >> "%systeminfo%"		)	) else (		for /f "tokens=1" %%A in ('wmic qfe get hotfixid') do (			set "hotfixid=%%~A"			if /i "!hotfixid:~0,2!" == "KB" (echo %%~A) >> "%systeminfo%"		)		set "hotfixid="	)	if not exist "%systeminfo%" echo.>"%systeminfo%"		rem Find msu files and save to file with the installed status.	for /R "%~dp0" %%U in (*.msu) do (		set installed=0						for /f "usebackq" %%I in ("%systeminfo%") do (			call :find "%%~nU" "%%I"			if not errorlevel 1 set installed=1		)				(echo !installed!	%%~U) >> "%updates%"	)	set "installed="):: View updates text file.echo #### BEFORE INSTALLStype "%updates%"for /f "usebackq tokens=1,2 delims=	" %%A in ("%updates%") do (	set "installed=%%~A"	set "filepath=%%~B"		rem Perform install if required and echo install status to the updates text file.	if "!installed!" == "0" (		set /a "count+=1"		if !count! leq %limit% (			ECHO wusa "!filepath!" /quiet /norestart			(echo 1	!filepath!) >> "%updates%.txt"		) else (			(echo 0	!filepath!) >> "%updates%.txt"		)	) else if "!installed!" == "1" (		(echo 1	!filepath!) >> "%updates%.txt"	) else (		(echo Unknown install status of "!filepath!") >&2	))set "installed="set "filepath="rem Move updates file to overwrite previous file.move /y "%updates%.txt" "%updates%" >nul:: View updates text file again.echo #### AFTER INSTALLStype "%updates%"endlocalgoto :eof:: Find a substring in a string by using substitution.:find	setlocal enabledelayedexpansion	set string=%~1	set substring=%~2	if /i "!string:%substring%=!" == "!string!" (		endlocal & exit /b 1	)	endlocalexit /b 0

This line below from the script will just echo the command for testing. Remove the ECHO keyword to enable wusa to execute.

            ECHO wusa "!filepath!" /quiet /norestart

Some of the TYPE commands can also be removed as they exist for validation purposes.

 

I have tested on some dummy msu files and it seems to be working ok. Try it and see how it performs for you.

Link to comment
Share on other sites


Wow Mhz.. really well done. I'm running into a few issues. Fist it closes out before I can see what it did, if anything. Can we pause it at the end?

It creates two text files

===system info==== which displays the updates installed?

KB2764913
KB2764916
13 listed.
 
=====updates.txt====which displays all the updates in the folder
1 E:\104 Updates\Win7 x86 Critical\IE11-Windows6.1-KB2909210-x86.msu
1 E:\104 Updates\Win7 x86 Critical\IE11-Windows6.1-KB3008923-x86.msu
1 E:\104 Updates\Win7 x86 Critical\Windows6.1-KB2479943-x86.msu
1 E:\104 Updates\Win7 x86 Critical\Windows6.1-KB2491683-x86.msu
45 more..
 
but it does nothing best I can tell. It appears to error somewhere and quit.
I'm providing this to my techs, and we go though about 5-10 computers a day with 2% re-install
I named them %computername%done & %computername%todo This way It will be different name with each computer.
I'm attempting to test this on the VMs I have running.
Edited by crobertson
Link to comment
Share on other sites

To pause the script, add pause before the goto :eof near the end of the script.

pauseendlocalgoto :eof

The systeminfo.txt file does have the installed updates. Once the updates.txt file is created then systeminfo.txt is not needed anymore so it could be deleted though I did not add that as it shows you the evidence of what happened.

 

The updates.txt file is a tab delimited file where the first column is 1 for installed and 0 as not installed yet. As an install happens then it will be recorded and the 0 will change to a 1. The 2nd column is the path to the msu files.

 

Remember to remove the ECHO that is in uppercase to execute wusa else it will just print the command to screen.

 

You can change the settings of the 4 variables named systeminfo, updates, limit and wmi at the top of the script. So you can change the systeminfo variable like below.

:: The filepath used to save the KB information.set "systeminfo=%~dp0%computername%.txt"

That will create the computername text file that you ask about.

 

Test output:

If I set limit to 2 and run the test, this is the print out.

#### BEFORE INSTALLS0       C:\Temp\CMD tests\crobertson\updates\a.msu0       C:\Temp\CMD tests\crobertson\updates\b.msu0       C:\Temp\CMD tests\crobertson\updates\c.msu1       C:\Temp\CMD tests\crobertson\updates\KB2841134.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849696.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849697.msuwusa "C:\Temp\CMD tests\crobertson\updates\a.msu" /quiet /norestartwusa "C:\Temp\CMD tests\crobertson\updates\b.msu" /quiet /norestart#### AFTER INSTALLS1       C:\Temp\CMD tests\crobertson\updates\a.msu1       C:\Temp\CMD tests\crobertson\updates\b.msu0       C:\Temp\CMD tests\crobertson\updates\c.msu1       C:\Temp\CMD tests\crobertson\updates\KB2841134.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849696.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849697.msu

2 installs done with 1 install left to do after 1st execution. The KB numbered files are detected as already installed so they are not installed by the script.

 

A 2nd run to test gives the output.

#### BEFORE INSTALLS1       C:\Temp\CMD tests\crobertson\updates\a.msu1       C:\Temp\CMD tests\crobertson\updates\b.msu0       C:\Temp\CMD tests\crobertson\updates\c.msu1       C:\Temp\CMD tests\crobertson\updates\KB2841134.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849696.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849697.msuwusa "C:\Temp\CMD tests\crobertson\updates\c.msu" /quiet /norestart#### AFTER INSTALLS1       C:\Temp\CMD tests\crobertson\updates\a.msu1       C:\Temp\CMD tests\crobertson\updates\b.msu1       C:\Temp\CMD tests\crobertson\updates\c.msu1       C:\Temp\CMD tests\crobertson\updates\KB2841134.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849696.msu1       C:\Temp\CMD tests\crobertson\updates\KB2849697.msu

So all installed in 2 executions. This is just echoing the wusa command.

Link to comment
Share on other sites

I have figured out there are two lines that it writes to the files %variable%.txt  So when %variable%=%computername%.txt the resultant file would be Computername.txt.txt

The program is writing correctly now, but all that displays is the screen showing what has/has not installed. It appears stalled and I see no progress. Is there a way we can see what is installing as it is being installed? 

Link to comment
Share on other sites

It appears stalled and I see no progress. Is there a way we can see what is installing as it is being installed? 

 

Above the line which is installing:

wusa "!filepath!" /quiet /norestart

Add something like:

ECHO; Installing !filepath!
Link to comment
Share on other sites

I have figured out there are two lines that it writes to the files %variable%.txt  So when %variable%=%computername%.txt the resultant file would be Computername.txt.txt

The program is writing correctly now, but all that displays is the screen showing what has/has not installed. It appears stalled and I see no progress. Is there a way we can see what is installing as it is being installed? 

I doubt the corrections you made are fixing anything. Seems you misunderstand the operation of what the script is doing. The delimited file of Computername.txt.txt is not a mistake, but rather deliberate with adding a 2nd extension on the original filename to create another filename for use. Example: Computername.txt is created with initial information. The information is used and changes are recorded to Computername.txt.txt. At the end of the script, Computername.txt.txt is moved over to overwrite Computername.txt. This is done as the script can not read and write to the same file with out messing it up. So Computername.txt.txt is actually a temporary file that later replaces the original file named Computername.txt. Perhaps I could have made a temporary filename more unique though I chose for an easy method.

 

I could summarize the script though you may not be able to link what words to what code. I will rather comment the code more and so that you can see what each line of code is doing. I included the pause at the end of the script and the progress output like how Yzöwl mentioned.

Heavier commented code:

Used line numbering so if you have further issues then perhaps you can reference which line could be causing it.

It may look better in a programming editor and so here is a download link for easy access.

Download the script

@echo offsetlocal enabledelayedexpansion:: Note: Remove the ECHO in front of the wusa keyword to enable installations.:: The delimiters in the saved files are tabs.:: Update the values of the next 4 set variables if needed.:::: The filepath used to save the KB information.set "systeminfo=%~dp0%computername%.txt":::: The filepath used to save the msu filepaths and the installed status.set "updates=%~dp0updates.txt":::: Limit number of KB hotfix installs during current script execution.set /a "limit=2":::: Use wmi instead of systeminfo. Use 0 for systeminfo.exe.set "wmi=1":: #### STEP 1.:: If file "%updates%" does not exist...:: - Get KB data and save to file "%systeminfo%".:: - Loop through msu files and record to file "%updates%" with install status and msu paths.:: Only do this if the file "%updates%" does not exist. Prevents repetition when the script is executed again.:: The file "%updates%" is read later in the script to determine if an install happens and if needed install, as to which msu fullpath is used.if not exist "%updates%" (		rem Find installed KB numbers from (wmi or systeminfo) and save to file.	if "%wmi%" == "0" (				rem Run systeminfo and save KB data to the "%systeminfo%" file.		for /f "tokens=2" %%A in ('systeminfo^|findstr /i "\<KB[0-9][0-9]*$"') do (			(echo %%A) >> "%systeminfo%"		)	) else (				rem Run wmic and save KB data to the "%systeminfo%" file. Note: Output from wmic ends with i.e nul or other that can cause issue.		for /f "tokens=1" %%A in ('wmic qfe get hotfixid') do (						rem Use a simple variable to check later if the value is actually a KB number.			set "hotfixid=%%~A"						rem Check first 2 characters are KB. If so, write to file "%systeminfo%".			if /i "!hotfixid:~0,2!" == "KB" (echo %%~A) >> "%systeminfo%"		)				rem Undefine variable.		set "hotfixid="	)		rem Create a empty "%systeminfo%" file if it does not exist. May prevent errors later if it did not exist.	if not exist "%systeminfo%" echo.>"%systeminfo%"		rem Find msu files and save to file with the installed status.	for /r "%~dp0" %%U in (*.msu) do (				rem Set installed to 0 as default for now.		set "installed=0"				rem Read each KB number from the "%systeminfo%" file.		for /f "usebackq" %%I in ("%systeminfo%") do (						rem Check if the KB number exists in the msu filename.			call :find "%%~nU" "%%I"						rem If the previous call sets errorlevel, then the msu file is assumed as not installed, else record it as installed.			if not errorlevel 1 set "installed=1"		)				rem Write install status and the full msu file path to the "%updates%" file.		(echo !installed!	%%~U) >> "%updates%"	)		rem Undefine variable.	set "installed="):: #### STEP 2.:: Perform installs if needed reading file "%updates%" and record install status changes to a temporary file "%updates%.tmp".:: Overwrite initial file "%updates%" with the temporary file "%updates%.tmp".:: End script when done.:: View updates text file.echo #### BEFORE INSTALLStype "%updates%":: Read install status and msu fullpath from the file "%updates%".for /f "usebackq tokens=1,2 delims=	" %%A in ("%updates%") do (		rem Add each line of read values into variable names that are easy to work with.	set "installed=%%~A"	set "filepath=%%~B"		rem Perform install if required and echo install status to the updates text file.	if "!installed!" == "0" (				rem Increment a count variable for use to later compare to a limit variable.		set /a "count+=1"				rem Check if count variable is less than or equal to the limit variable.		if !count! leq %limit% (						rem Progress output as like Yzöwl suggested.			echo ## INSTALLING "!filepath!"						rem Install the msu file with parameters. Remove leading ECHO to run the wusa command.			ECHO wusa "!filepath!" /quiet /norestart						rem Record as installed to a temporary file.			(echo 1	!filepath!) >> "%updates%.tmp"		) else (						rem Out of limit bounds so it can be installed on the next run of the script perhaps.			rem Record as not installed to a temporary file.			(echo 0	!filepath!) >> "%updates%.tmp"		)			rem Known as already installed.	) else if "!installed!" == "1" (				rem Record as installed to a temporary file.		(echo 1	!filepath!) >> "%updates%.tmp"	) else (				rem This should not happen. Though, it could be recorded with a install status of 2?		(echo Unknown install status of "!filepath!") >&2	))rem Undefine variables.set "count="set "installed="set "filepath="rem Move temporary file "%updates%.tmp" to overwrite the file "%updates%".move /y "%updates%.tmp" "%updates%" >nul:: View updates text file again.echo #### AFTER INSTALLStype "%updates%"rem Optional pause as requested.pauserem End of script using goto :eof.endlocalgoto :eof:: The label :find works like a function that can be called as may times as needed.:: Find a substring in a string by using variable substitution.:find	setlocal enabledelayedexpansion		rem Define some variables that are useful for variable substitution.	set "string=%~1"	set "substring=%~2"		rem If the KB number is not a substring i.e substitution fails, then set errorlevel to 1.	if /i "!string:%substring%=!" == "!string!" (		endlocal & exit /b 1	)	endlocalrem set errorlevel to 0.exit /b 0

 

Edit: Upon review and to avoid further confusion with the .txt and .txt.txt use of file handling, I have done a small change by using .txt and .txt.tmp as the later has a better reference to actually being a temporary file.

Edited by MHz
Link to comment
Share on other sites

  • 4 months later...

hi,Yzowl. I am a newbe here..your code is awesome :thumbup thanks a lot.

But I just got a little problem,when I tested this batch by choosing "run as administrator"(windows7 x64) in a folder with all MSU and EXE update files, .bat processed and listed them,

but in fact none of them was actuelly installed. Then I tried to add "cd /d %~dp0" to change the path at the beginning before your code, and it worked finally.

But in your code these is already "FOR /R "%~dp0" %%A IN (*-KB*.MSU) DO (" , looks like this parte doesn't work for me, I dont understand why, could you please explain?

PS: your second code for .exe file updating woks well.

 

That is correct.

Do bear in mind that WUSA.EXE is located within %PATH% as is SHUTDOWN.EXE and that executables located within the path do not require their path prepending to the command. You have done so with one and not the other, whilst both are correct it seems pointless to include that which isn't required.

You can also simplify lines 2 & 3 by merging them into one thus

@ECHO OFFSETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSIONFOR /R "%~dp0" %%A IN (*-KB*.MSU) DO (	CALL :SUB %%~nA	ECHO= Installing KB!KB_NUM!	>NUL TIMEOUT /t 3	WUSA "%%A" /quiet /norestart)ECHO= == Press any key to restart ==>NUL PAUSESHUTDOWN.EXE /r /t 0GOTO :EOF:SUBSET "KB_NUM=%*"FOR /F "DELIMS=-" %%B IN ("%KB_NUM:*-KB=%") DO SET "KB_NUM=%%B"

 

Hello how can i change the script not to restart after it finish and just close/exit the script??

Edited by dimitris77
Link to comment
Share on other sites

:crazy: Change it to the following or just delete the line.

REM SHUTDOWN.EXE /r /t 0
"REM" makes it a Comment line. The characters "::" instead of "REM" work the same way.

 

*HOWEVER* I wouldn't recommend that since there will be Pending Actions that will *NOT* occur until you Reboot.

Link to comment
Share on other sites

  • 9 months later...

Yzowl you are a genius!

Your batch file has eliminated all headaches of updating my Windows 7.

I use Windows Updates Downloader program to get ALL past updates,

and your batch file installs them all in a few minutes from various folders!

Thank you so much man....for sharing your fabulous program.

Link to comment
Share on other sites

Hi all.

I've been reading the posts here & it seems like the post & batch file that's working for me is the *.msu one showing from page 1 & as shown below.
It's not working yet, but this a concept. I hope someone can help edit it from here. But as also mentioned by "centi50" he would also need the batch
file to run both *.exe & *.msu files together silently.

If I run it one kind of it's own, it's working, I'm sure there's a way for a combination of 2 in any order, any takers? Meaning both the *.exe files
& *.msu are in the same folder & will still run until the last patch? And then I need 2 text files to show the details of success or failed 1 showing
all the *.exe & 1 showing *.msu files. And if it's installed before or not needed on that OS (windows 7, Server 2008 or whatever OS) it would also
say something like "installed", "not required" or something. I would appreciate if someone can help, thanks.

Can edit from existing below
==================

echo off
for /f %%A in ('dir /b *.msu') do (
echo Installing Updates "%%A" ... >>msu_list.txt
start /wait %%A /quiet /norestart > nul
)

else

for /f %%A in ('dir /b *.exe') do (
echo Installing Updates "%%A" ... >>exe_list.txt
start /wait %%A /quiet /norestart > nul
)

echo.
echo ###########################################################
echo.
echo Updates installed
echo Press any key to restart
pause >NUL

echo.
rem I may or maynot need to restart so I "rem" it out first.
rem shutdown.exe /r /t 0
Edited by AcidIce
Link to comment
Share on other sites

  • 11 months later...

Hi all :)

I'm a French excuse my English ^^

based of the Yzöwl script :

Quote

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
FOR /R "%~dp0" %%A IN (*.MSU) DO (CALL :SUB %%~nA
ECHO INSTALLING KB!KB_number!
TIMEOUT /t 3 >NUL
C:\Windows\System32\wusa "%%A" /quiet /norestart)
ECHO == Press any key to restart ==&PAUSE>NUL
shutdown.exe /r /t 0
GOTO :EOF

:SUB
SET "KB_number=%*"
FOR /F "DELIMS=-" %%B IN ("%KB_number:*-KB=%") DO SET "KB_number=%%B"

I looking for compare with a list of installed KB (exported by "wmic qfe GET hotfixid > kb.txt" command) on the target before launch

Quote

FOR /R "%~dp0" %%A IN (*.MSU) DO (CALL :SUB %%~nA
ECHO INSTALLING KB!KB_number!
TIMEOUT /t 3 >NUL
C:\Windows\System32\wusa "%%A" /quiet /norestart)
ECHO == Press any key to restart ==&PAUSE>NUL
shutdown.exe /r /t 0
GOTO :EOF

:SUB
SET "KB_number=%*"
FOR /F "DELIMS=-" %%B IN ("%KB_number:*-KB=%") DO SET "KB_number=%%B"

here, my modified script who doesn't work  :

SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
wmic qfe GET hotfixid > kb.txt
IF
FOR /R "%~dp0" %%A IN (kb.txt) DO (

    IF %%A EQU kb.txt GOTO :EOF ELSE (CALL :SUB1 %%~nA
                                        ECHO INSTALLING KB!KB_number!
                                        TIMEOUT /t 3 >NUL
                                        C:\Windows\System32\wusa "%%A" /quiet /norestart))
ECHO == Press any key to restart ==&PAUSE>NUL
shutdown.exe /r /t 0

:SUB
SET "KB_number=%*"
FOR /F "DELIMS=-" %%B IN ("%KB_number:*-KB=%") DO SET "KB_number=%%B"

Can you help me please ?

Thank you so much

Gratefully

Guillaume

Link to comment
Share on other sites

Guillaume.

you have an excess "IF" (on a line by itself) and, besides that, you should check the FOR syntax.

The /R switch applies to "each item in files" while you are using it for a "list of names of files" (the kb.txt), see:

https://ss64.com/nt/for.html

Post  the first few lines of your kb.txt, so that I can understand if they include a path, etc.

jaclaz


 

Link to comment
Share on other sites

  • 4 weeks later...

@nerio Here's a quick untested rewrite. (I have included code to do this for exe's and msu's containing the string -KB)

It should auto create the "kb.txt" file, install only the msu's which were not output in that list before deleting the list.

@ECHO OFF
SETLOCAL ENABLEEXTENSIONS DISABLEDELAYEDEXPANSION

IF /I NOT "%~dp0"=="%__CD__%" (PUSHD "%~dp0" 2>NUL && (SET _$=T) || GOTO :EOF)

(	FOR /F "EOL=H DELIMS=" %%# IN ('WMIC.EXE QFE GET HOTFIXID') DO FOR %%$ IN (
	%%#) DO ECHO=%%$)>"KB.TXT"

IF NOT EXIST "KB.TXT" GOTO ENDIT

SET "_=T"
FOR /F "DELIMS=" %%# IN ('WHERE/F /R . *-KB*.MSU *-KB*.EXE') DO (SET "_N=%%~n#"
	IF /I "%%~x#"==".MSU" SET "_X=WUSA.EXE"
	SETLOCAL ENABLEDELAYEDEXPANSION
	FINDSTR/IBC:"!_N:*-KB=KB!" "KB.TXT">NUL 2>&1 && (ENDLOCAL) || (
		ECHO=INSTALLING !_N:*-KB=KB!
		TIMEOUT 2 /NOBREAK>NUL
		!_X! "%%#" /quiet /norestart
		ENDLOCAL & SET "_="))

DEL "KB.TXT"
IF DEFINED _ GOTO ENDIT

ECHO= == Press any key to restart ==
PAUSE>NUL
SHUTDOWN.EXE /r /t 0

:ENDIT
IF "%_$%"=="T" POPD
GOTO :EOF

Note: The above uses the WHERE and TIMEOUT commands with MSU's which were all post XP, if you are using XP then you'd need to replace:

FOR /F "DELIMS=" %%# IN ('WHERE/F /R . *-KB*.MSU *-KB*.EXE') DO (SET "_N=%%~n#"
	IF /I "%%~x#"==".MSU" SET "_X=WUSA.EXE"
	SETLOCAL ENABLEDELAYEDEXPANSION
	FINDSTR/IBC:"!_N:*-KB=KB!" "KB.TXT">NUL 2>&1 && (ENDLOCAL) || (
		ECHO=INSTALLING !_N:*-KB=KB!
		TIMEOUT 2 /NOBREAK>NUL
		!_X! "%%#" /quiet /norestart
		ENDLOCAL & SET "_="))

with:

FOR /F "DELIMS=" %%# IN ('DIR/B/S/A-D *-KB*.EXE') DO (SET "_N=%%~n#"
	SETLOCAL ENABLEDELAYEDEXPANSION
	FINDSTR/IBC:"!_N:*-KB=KB!" "KB.TXT">NUL 2>&1 && (ENDLOCAL) || (
		ECHO=INSTALLING !_N:*-KB=KB!
		>NUL PING -n 3 0.0.0.0
		"%%#" /quiet /norestart
		ENDLOCAL & SET "_="))
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...