Find version of installed program from batch file

We have a batch file that installs several programs as part of a developer setup. This is done periodically when we get new versions of the components used. Therefore, it would be nice to establish if the versions are different.

On the command line, I can run this and return the installed version:

wmic datafile where name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe' get version /format:list 

Which gives the output Version=12.1.369.0 .

However, when I put this in a batch file, like this one, and try to extract the version:

 echo off FOR /F "tokens=2 delims==" %%I in ('"wmic datafile where^(name^="C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe" get version /format:list"') DO (SET "RESULT=%%I") ECHO %RESULT% 

I get the answer \\Common was unexpected at this time.

Some parts may be redundant as I try to remove the "Network" to fix this.

What did I miss?

+6
source share
5 answers

You have a set of non-local double quotes, as well as an optional ( .

WMIC uses SQL syntax, and strings are enclosed in single quotes. Internal single quotes do not interfere with a command containing single quotes.

You can put double quotation marks in the WHERE clause (not including the WHERE keyword) to avoid some escaping problems in the FOR DO () clause.

 @echo off FOR /F "tokens=2 delims==" %%I IN ( 'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list' ) DO SET "RESULT=%%I" ECHO %RESULT% 

But this may not be the solution. You cannot see this with the code above, but RESULT actually contains a return carriage return (0x0D). This is due to the quirk of how FOR / F handles WMIC unicode output. An additional carriage return will be added to each line of WMIC output.

As long as you always access RESULT with %RESULT% (normal extension), then you will not have any problems. But if you ever need a delayed extension, then you may have problems, as shown below.

 @echo off setlocal enableDelayedExpansion FOR /F "tokens=2 delims==" %%I IN ( 'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list' ) DO SET "RESULT=%%I" ECHO %RESULT%xxx ECHO !RESULT!xxx 

One convenient way to remove unwanted carriage returns is to use an extra FOR level.

 @echo off setlocal enableDelayedExpansion FOR /F "tokens=2 delims==" %%I IN ( 'wmic datafile where "name='C:\\Program Files (x86)\\Common Files\\Company\\Product\\Version12\\Product.exe'" get version /format:list' ) DO FOR /F "delims=" %%A IN ("%%I") DO SET "RESULT=%%A" ECHO %RESULT%xxx ECHO !RESULT!xxx 
+7
source

Here is the routine I use for this in my own version of the software update script:

 :getfattr set %1= setlocal set "name=%~f2" set "name=%name:\=\\%" for /f "delims=" %%A in ('wmic datafile where "name='%name:'=\'%'" get %1 /format:list') do @^ for /f "delims=" %%B in ("%%A") do endlocal & set "%%B" & goto :eof echo>&2 getfattr failed endlocal goto :eof 

It can get any file attribute supported by wmic datafile get . For example, here's how you can get the version of a file for installed Adobe Reader:

 call :getfattr version "%ProgramFiles(x86)%\Adobe\Reader 11.0\Reader\AcroRd32.exe" echo "!version!" 

After that, the environment variable version will contain the requested version string. If :getfattr does not work, version guaranteed to be canceled.

The trace of the test execution for this example looks like this (slow extension is already enabled, but this is not assumed: getfattr):

 >call :getfattr version "C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe" >set version= >setlocal >set "name=C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe" >set "name=C:\\Program Files (x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe" >for /F "delims=" %A in ('wmic datafile where "name='C:\\Program Files (x86)\\Adobe\\Reader 11.0\\Reader\\AcroRd32.exe'" get version /format:list') do @for /F "delims=" %B in ("%A") do endlocal & set "%B" & goto :eof >endlocal & set "Version=11.0.18.21" & goto :eof >echo "!version!" "11.0.18.21" 

As you can see, this is pretty straightforward and doesn't hurt too much. He, however, tiptoes through the minefield cmd and wmic gotchas.

Firstly, the name of the attribute you want to get is also the name used for the variable you want to get the result in ( version in the above test). Inside the routine, this is the name %1 , so set %1= clears it.

The file name that you transfer needs some pre-processing before it can be safely transferred to wmic , and this requires a shell variable, so setlocal is issued to avoid topping the caller’s variables.

set "name=%~f2" copies the name to the environment variable after deleting all surrounding double quotes and expanding it to the full path name. Double quotes surround the entire set argument to prevent grief caused by ampersands or parentheses in the path names.

wmic queries use SQL-type syntax, where string values ​​are surrounded by single quotes ' , and \ is escape, which suppresses any special value for the next character. Since both of them are legal in Windows paths, all occurrences either need the \ prefix. set "name=%name:\=\\%" hides the built-in backslashes, and the constructor '%name:'=\'%' on the wmic command line avoids the built-in single quotes and adds the required surrounding.

cmd parser does not disable special processing between single quotes, and the name no longer contains any close double quotes, so embedded spaces, brackets, or ampersands can potentially disrupt the operation. To guard against this, the wmic argument of integer name= gets double quotes. There is no need for special processing for double quotes already inside the name, because double quotes are not allowed in Windows file names, so they cannot be.

The for command line containing the wmic command ends with the sequence @^ . ^ serves to append the next line as the payload of the external for command; @ prevents an echo in the execution trace, even if ECHO is enabled.

This echo cancellation occurs mainly because the internal for exists only to get rid of the false CR characters introduced by the cmd error converting wmic output from Unicode to ASCII (the same method used by @dbenham answer), and if it allowed an echo These CRs simply drown out the track with confusing rewriting. As a side benefit, the internal for will not perform its own payload if the line it passed from the external for contains only CR, a version-specific number that wmic insists on emitting. The internal for payload receives an echo if ECHO is enabled, so tracing still captures all the useful events.

This payload consists of three & -separable commands, which for will expand as a single command line before cmd receives the processing of individual commands. In particular, this means that set "%%B" expands to run endlocal , which puts the variable created by this set outside the scope of setlocal / endlocal and makes it available to the caller.

%%B will always expand in the format name = value due to the /format:list switch passed to wmic ; the name will be the same as specified in the verb get , and that is how the name you pass ends up picking the variable you are returning. The entire argument name = value for set quoted if the requested attribute contains special shell characters. It does: getfattr itself is safe, but you can use it! Delayed! expansion, not% premature increase%, wherever you use the variable that it returns to you.

& goto :eof on the same line breaks off from both for chains and returns to: getfattr caller, as soon as the internal one actually does something, just in case you pass in some strange name and wmic get produces more, than one non-empty line of output.

The last three lines are executed only if wmic does not display output at all, what happens when it fails.

+2
source

This is a file with a file file system.

 @echo off If "%~1"=="" goto help If "%~1"=="/?" goto help If /i "%~1"=="/h" goto help If "%~1"=="-?" goto help If /i "%~1"=="-h" goto help set filepath=%~f1 set file=%filepath:\=\\% wmic datafile where name^="%file%" get version|findstr /i /v /c:"version" echo %errorlevel% goto finish :help Echo. Echo. FileVer Echo. ------- Echo. Echo. Purpose: Echo. Echo. Reports the version number for an exe, dll, and similar files. Echo. Echo. Usage: Echo. Echo. filever ^<executable file^> Echo. Echo. filever [/h ^| -h ^| /? ^| -?] Starts this help Echo. echo. Examples: Echo. Echo. filever c:\windows\explorer.exe Echo. filever "C:\Program Files\Windows NT\Accessories\wordpad.exe" Echo. filever shell32.dll Echo. Echo. For Help Echo. Echo. filever Echo. filever /? Echo. :finish rem Pause if command double clicked If /i "%cmdcmdline:~0,6%"=="cmd /c" pause 

You seem to have an extra set of quotes throughout the command

One problem with loops is wmic outpus empty line after version.

 set filepath=%~f1 set file=%filepath:\=\\% for /f "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"') do set a=%%A & echo %%A 

Put it in a file. Although the file has two empty lines.

 set filepath=%~f1 set file=%filepath:\=\\% wmic datafile where name^="%file%" get version|findstr /i /v /c:"version">test.txt 

Maybe it might work

 set filepath=%~f1 set file=%filepath:\=\\% for /f "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"') do if not ""==%%A set a=%%A & echo %%A 

Or call another batch file and not return.

Here you can get blank lines.

 set filepath=%~f1 set file=%filepath:\=\\% for /f "tokens=1 eol= " %%A in ('wmic datafile where name^="%file%" get version^|findstr /i /v /c:"version"^|findstr /i /v /r "^$"') do set a=%%A & echo %A% wmic datafile where name^="%file%" get version|findstr /i /v /c:"version"|findstr /i /v /r "^$">test.txt 
0
source

Here is an alternative method that bypasses WMIC for powershell Get-WmiObject.

 @ECHO OFF start /b powershell.exe -command "Get-WmiObject -Class CIM_DataFile -Filter \"Name='C:\\Program Files (x86)\\Knuckle.dll'\" | Select-Object Version" PAUSE 

Returns this result when double-clicking the .cmd batch file.

 Version ------- 0.0.0.0 
0
source

and two ways without external tools 1. WMIC

 WMIC DATAFILE WHERE name="C:\\install.exe" get Version /format:Textvaluelist 

Note the double slashes of the file name.

2. MakeCAB since WMIC is not installed in home versions of windows here, using makecab that will run on every Windows machine:

 ; @echo off ;;goto :end_help ;;setlocal DsiableDelayedExpansion ;;; ;;; ;;; fileinf /l list of full file paths separated with ; ;;; fileinf /f text file with a list of files to be processed ( one on each line ) ;;; fileinf /? prints the help ;;; ;;:end_help ; REM Creating a Newline variable (the two blank lines are required!) ; set NLM=^ ; set NL=^^^%NLM%%NLM%^%NLM%%NLM% ; if "%~1" equ "/?" type "%~f0" | find ";;;" | find /v "find" && exit /b 0 ; if "%~2" equ "" type "%~f0" | find ";;;" | find /v "find" && exit /b 0 ; setlocal enableDelayedExpansion ; if "%~1" equ "/l" ( ; set "_files=%~2" ; echo !_files:;=%NL%!>"%TEMP%\file.paths" ; set _process_file="%TEMP%\file.paths" ; goto :get_info ; ) ; if "%~1" equ "/f" if exist "%~2" ( ; set _process_file="%~2" ; goto :get_info ; ) ; echo incorect parameters & exit /b 1 ; :get_info ; set "file_info=" ; makecab /d InfFileName=%TEMP%\file.inf /d "DiskDirectory1=%TEMP%" /f "%~f0" /f %_process_file% /v0>nul ; for /f "usebackq skip=4 delims=" %%f in ("%TEMP%\file.inf") do ( ; set "file_info=%%f" ; echo !file_info:,=%nl%! ; ) ; endlocal ;endlocal ; del /q /f %TEMP%\file.inf 2>nul ; del /q /f %TEMP%\file.path 2>nul ; exit /b 0 .set DoNotCopyFiles=on .set DestinationDir=; .set RptFileName=nul .set InfFooter=; .set InfHeader=; .Set ChecksumWidth=8 .Set InfDiskLineFormat=; .Set Cabinet=off .Set Compress=off .Set GenerateInf=ON .Set InfDiskHeader=; .Set InfFileHeader=; .set InfCabinetHeader=; .Set InfFileLineFormat=",file:*file*,date:*date*,size:*size*,csum:*csum*,time:*time*,vern:*ver*,vers:*vers*,lang:*lang*" 

Example output (it has a lowercase version, which is a small addition to the wmic method :)):

 c:> fileinfo.bat /l C:\install.exe file:install.exe date:11/07/07 size:562688 csum:380ef239 time:07:03:18a vern:9.0.21022.8 vers:9.0.21022.8 built by: RTM lang:1033 

3 Using shell.application and the hybrid batch \ jscript.Here tooptipInfo.bat :

 @if (@X)==(@Y) @end /* JScript comment @echo off rem :: the first argument is the script name as it will be used for proper help message cscript //E:JScript //nologo "%~f0" %* exit /b %errorlevel% @if (@X)==(@Y) @end JScript comment */ ////// FSOObj = new ActiveXObject("Scripting.FileSystemObject"); var ARGS = WScript.Arguments; if (ARGS.Length < 1 ) { WScript.Echo("No file passed"); WScript.Quit(1); } var filename=ARGS.Item(0); var objShell=new ActiveXObject("Shell.Application"); ///// //fso ExistsItem = function (path) { return FSOObj.FolderExists(path)||FSOObj.FileExists(path); } getFullPath = function (path) { return FSOObj.GetAbsolutePathName(path); } // //paths getParent = function(path){ var splitted=path.split("\\"); var result=""; for (var s=0;s<splitted.length-1;s++){ if (s==0) { result=splitted[s]; } else { result=result+"\\"+splitted[s]; } } return result; } getName = function(path){ var splitted=path.split("\\"); return splitted[splitted.length-1]; } // function main(){ if (!ExistsItem(filename)) { WScript.Echo(filename + " does not exist"); WScript.Quit(2); } var fullFilename=getFullPath(filename); var namespace=getParent(fullFilename); var name=getName(fullFilename); var objFolder=objShell.NameSpace(namespace); var objItem=objFolder.ParseName(name); //https://msdn.microsoft.com/en-us/library/windows/desktop/bb787870(v=vs.85).aspx WScript.Echo(fullFilename + " : "); WScript.Echo(objFolder.GetDetailsOf(objItem,-1)); } main(); 

used for cmd.exe:

 C:\Windows\System32\cmd.exe : File description: Windows Command Processor Company: Microsoft Corporation File version: 6.3.9600.16384 Date created: ?22-?Aug-?13 ??13:03 Size: 347 KB 
0
source

Source: https://habr.com/ru/post/973424/


All Articles