Узнать версию установленной программы из пакетного файла

у нас есть пакетный файл, который устанавливает несколько программ в рамках установки разработчиков. Это выполняется периодически, когда мы получаем новые версии используемых компонентов. Поэтому было бы неплохо установить, если версии разные.

в командной строке я могу запустить это и вернуть установленную версию:

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

который дает выход Version=12.1.369.0.

однако, когда я помещаю это в пакетный файл, подобный этому, и пытаюсь извлечь версия:

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

Я получаю ответ Common was unexpected at this time.

некоторые части могут быть избыточными, поскольку я пробовал вещи из сети, чтобы исправить это.

что я пропустила?

5 ответов


у вас есть набор неуместных двойных кавычек, а также дополнительный (.

WMIC использует синтаксис SQL, а строки заключены в одинарные кавычки.Внутренние одинарные кавычки не мешают команде, заключающей одинарные кавычки.

вы можете поместить двойные кавычки вокруг предложения WHERE (не включая ключевое слово WHERE), чтобы избежать некоторых проблем с escape в предложении for DO ().

@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%

но это может быть не совсем все решение. Ты не видишь этого. с вышеуказанным кодом, но результат фактически содержит возврат завершающей каретки (0x0D). Это связано с причудой с тем, как FOR /F обрабатывает вывод wmic unicode. Каждая строка вывода WMIC будет иметь дополнительный возврат трейлинг-каретки.

пока вы всегда получаете доступ к результату с помощью %RESULT% (нормальное расширение), тогда у вас не будет никаких проблем. Но если вам когда-либо понадобится отложенное расширение, у вас могут возникнуть проблемы, как показано ниже.

@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

один удобный способ для удаления нежелательного возврата каретки необходимо использовать дополнительный уровень FOR.

@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

вот подпрограмма, которую я использую для этого в моем собственном пакетном скрипте обновления программного обеспечения:

: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

он может получить любой атрибут файла, поддерживаемый wmic datafile get. Например, вот как вы можете получить версию файла для установленного Adobe Reader:

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

после этого переменная среды version будет содержать запрошенную строку версии. Если :getfattr не, version гарантированно будет unset.

трассировка выполнения теста для этого пример выглядит так (отложенное расширение уже включено, хотя это не предполагается :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"

как вы можете видеть, это довольно прямо и не слишком много. Тем не менее, он на цыпочках проходит через минное поле cmd и wmic подводных камней.

во-первых, имя атрибута, который вы хотите получить, также является именем, используемым для переменной, в которой вы хотите получить результат (version в тесте выше). Внутри подпрограммы это имя %1, так что set %1= очищает его.

имя файла, которое вы передаете, нуждается в предварительной обработке, прежде чем его можно будет безопасно передать wmic и для этого требуется переменная оболочки, поэтому setlocal выдается, чтобы избежать топтания переменных вызывающего абонента.

set "name=%~f2" копирует имя в переменную среды после удаления всех окружающих двойных кавычек и расширения его до полного пути. Двойные кавычки окружают весь set аргумент для предотвращения горя, вызванного амперсандами или круглые скобки в именах путей.

wmic запросы используют SQL-подобный синтаксис, где строковые значения окружены одинарной кавычкой ' символы и \ - это побег, который подавляет любое особое значение следующего символа. Поскольку оба они являются законными в именах путей Windows, все вхождения либо нуждаются в \ префикс. set "name=%name:\=\%" escapes встроенные обратные косые черты и '%name:'=\'%' построить в wmic командная строка экранирует встроенные одинарные кавычки и добавляет требуются окружающие.

cmdпарсер не отключает специальную обработку между одинарными кавычками, и имя больше не имеет никаких окружающих двойных кавычек, поэтому встроенные пробелы, круглые скобки или амперсанды могут потенциально нарушать вещи. Чтобы защититься от этого, wmicвсю name= аргумент получает двойную кавычку. Нет необходимости в специальной обработке для двойных кавычек уже внутри имени, потому что двойные кавычки are запрещено в именах файлов Windows так их не может быть.

на for командная строка, содержащая заканчивается @^ последовательности. The ^ служит для присоединения следующей строки в качестве полезной нагрузки внешнего ; С @ предотвращает эхо этой полезной нагрузки в трассировке выполнения, даже если Эхо включено.

это подавление Эха делается главным образом потому, что внутреннее for существует только для того, чтобы избавиться от ложных символов CR, введенных cmdбагги преобразование wmicвывод из Unicode в ASCII (тот же метод, что и в ответе @dbenham), и если разрешено Эхо, эти CRs просто загрязняют трассировку с запутанными перезаписями. Как побочное преимущество, внутреннее for не будет выполнять свою собственную полезную нагрузку, когда линия передана из внешнего for содержит только CR, зависящий от версии номер которого wmic настаивает на ИСП. Внутреннее forС нагрузкой тут получить Эхо, если Эхо включено, поэтому трассировка все еще захватывает все полезные события.

что груз состоит из трех разделенных команд, которые for будет расширяться как одна командная строка перед cmd получает для обработки отдельных команд. В частности, это означает, что set "%%B" расширяется до endlocal выполняется, что ставит переменную, созданную этим set за пределами setlocal/endlocal объем и делает его доступным для абонента.

%%B всегда будет расширяться в формате name=value из-за /format:list переключатель перешел в wmic; имя будет таким же, как указано в get глагол, и вот как имя, которое вы передаете, в конечном итоге выбирает переменную, которую вы возвращаете. Весь name=value до set цитируется в случае, если запрошенный атрибут содержит специальные символы оболочки. Это делает: getfattr безопасным, но вы можете использовать !отложено! расширение, а не % преждевременное % расширение везде, где вы на самом деле используйте переменную, которую он возвращает вам.

на & goto :eof на этой же строке разрывается от обоих for петли и возвращается к: вызывающий getfattr, как только внутренний на самом деле что-то делает, на случай, если вы передадите какое-то странное имя и wmic get производит больше чем одну non-пустую линию выхода.

последние три строки только когда-либо работать, если wmic не производит никакого вывода вообще, что и происходит, когда он терпит неудачу.


Это мой файл filever bat.

@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

кажется, у вас есть дополнительный набор кавычек вокруг всей команды

одна проблема с циклами for-это wmic outpus пустая строка после версии.

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

поместите его в файл. Хотя в файле две пустые строки.

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

возможно, это может сработать

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

или вызовите другой пакетный файл и не возвращайтесь.

вот способ получить пустой русло.

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

вот альтернативный метод, обходит WMIC для 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

возвращает этот результат при двойном щелчке a .пакетный файл cmd.

Version
-------
0.0.0.0

и два способа без внешних инструментов 1.WMIC

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

обратите внимание на двойные слэши в имени файла.

2.MAKECAB поскольку WMIC не установлен на домашних версиях windows, вот способ с makecab, который будет работать на каждой машине windows:

; @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*"

пример вывода (он имеет строковую версию, которая является небольшим дополнением к методу wmic:)):

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 используя shell.применение и гибридный пакет\jscript.Вот!--6-->tooptipInfo.летучая мышь!--7--> :

@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();

используется против 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