web-dev-qa-db-fra.com

Trouver la version d'un programme installé à partir d'un fichier de commandes

Nous avons un fichier de commandes qui installe plusieurs programmes dans le cadre de l’installation des développeurs. Ceci est exécuté périodiquement lorsque nous obtenons de nouvelles versions des composants utilisés. Donc, ce serait bien d'installer seulement si les versions sont différentes.

À l'invite de commande, je peux l'exécuter et récupérer la version installée:

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

Ce qui donne le Version=12.1.369.0 en sortie.

Cependant, lorsque je mets cela dans un fichier de commandes comme celui-ci et que je tente d'extraire la 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%

Je reçois la réponse \\Common was unexpected at this time.

Certaines parties peuvent être redondantes, car j’ai essayé d’essayer sur Internet pour corriger cela.

Qu'est-ce que j'ai manqué?

6
graham.reeds

Vous avez un ensemble de guillemets doubles égarés, ainsi qu'un ( supplémentaire.

WMIC utilise la syntaxe SQL et les chaînes sont entre guillemets simples. Les guillemets simples internes n'interfèrent pas avec la commande qui entoure les guillemets simples.

Vous pouvez mettre des guillemets autour de la clause WHERE (sans le mot clé WHERE) pour éviter certains problèmes d'échappement au sein de la clause 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%

Mais ce n'est peut-être pas tout à fait la solution. Vous ne pouvez pas le voir avec le code ci-dessus, mais RESULT contient en réalité un retour chariot final (0x0D). Cela est dû à un hasard avec la façon dont FOR/F gère la sortie WMIC unicode. Chaque ligne de sortie WMIC aura le retour supplémentaire par chariot.

Tant que vous accédez toujours à RESULT en utilisant %RESULT% (expansion normale), vous n'aurez aucun problème. Mais si vous avez besoin d'une expansion retardée, vous pouvez avoir des problèmes, comme illustré ci-dessous.

@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

Une méthode pratique pour éliminer le retour de chariot indésirable consiste à utiliser un niveau supplémentaire de 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
8
dbenham

Voici le sous-programme que j'utilise pour cela dans mon propre script de traitement par lots de mises à jour logicielles:

: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

Il peut obtenir n'importe quel attribut de fichier pris en charge par wmic datafile get. Par exemple, voici comment vous pouvez obtenir la version du fichier pour Adobe Reader actuellement installé:

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

Après cela, la variable d’environnement version contiendra la chaîne de version demandée. Si :getfattr échoue, il est garanti que version sera inactif.

Une trace d'exécution de test pour cet exemple ressemble à ceci (le développement différé était déjà activé, bien que cela ne soit pas supposé par: 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"

Comme vous pouvez le constater, c’est assez direct et ça ne dérange pas trop. Cependant, il parcourt la zone de mines de cmd et wmic pièges.

Premièrement, le nom de l'attribut que vous voulez obtenir est également le nom utilisé pour la variable dans laquelle vous voulez que le résultat aboutisse (version dans le test ci-dessus). Dans la sous-routine, ce nom est %1, donc set %1= l'efface.

Le nom de fichier que vous transmettez a besoin d'un peu de prétraitement avant de pouvoir être remis en toute sécurité à wmic et une variable Shell est nécessaire à cet effet. Par conséquent, setlocal est émis pour éviter de piétiner les variables de l'appelant.

set "name=%~f2" copie le nom dans une variable d’environnement après avoir enlevé les guillemets et les avoir étendus à un chemin complet. Les guillemets doubles entourent tout l'argument set pour éviter les problèmes causés par les esperluettes ou les parenthèses dans les noms de chemin.

Les requêtes wmic utilisent une syntaxe de type SQL, dans laquelle les valeurs de chaîne sont entourées de guillemets simples ' et \ est un d'échappement qui supprime toute signification spéciale du caractère suivant. Comme ces deux noms sont légaux dans les chemins d'accès Windows, toutes les occurrences de l'un ou de l'autre ont besoin d'un préfixe \. set "name=%name:\=\\%" échappe aux barres obliques inverses incorporées et la construction '%name:'=\'%' dans la ligne de commande wmic échappe aux guillemets simples incorporés et ajoute les guillemets requis.

L’analyseur de cmd n’active pas le traitement spécial entre guillemets simples et le nom ne comporte plus de guillemets, ce qui permet aux espaces, parenthèses ou esperluettes incorporés de se casser. Pour se prémunir contre cela, la totalité de l'argument name= de wmic est citée en double. Il n'y a pas besoin de traitement spécial pour les guillemets doubles déjà dans le nom, car les guillemets are sont interdits dans les noms de fichiers Windows, de sorte qu'il ne peut en exister aucun.

La ligne de commande for contenant la commande wmic se termine par une séquence @^. Le ^ sert à attacher la ligne suivante comme charge utile de la commande externe for; le @ empêche que l'échec de la charge utile dans une trace d'exécution, même si ECHO est activé.

Cette suppression d’écho est effectuée principalement parce que la variable for intérieure n’existe que pour éliminer les caractères CR parasites injectés par la conversion boguée de cmd de la sortie de wmic de Unicode en ASCII (même technique que celle utilisée dans la réponse de @ dbenham) pour faire écho, ces CR juste salir la trace avec écrasements déroutants. En plus, for interne n'exécutera pas sa propre charge lorsque la ligne for externe contenue contient seulement un CR, un nombre dépendant de la version dont wmic insiste pour émettre. La charge utile do de for intérieur est répercutée si ECHO est activé, donc le traçage capture toujours tous les événements utiles.

Cette charge se compose de trois commandes séparées, que for développera comme une seule ligne de commande avant que cmd puisse traiter les commandes individuelles. Cela signifie notamment que set "%%B" est développé avant l'exécution de endlocal, ce qui place la variable créée par ce set en dehors de la portée setlocal/endlocal et la met à la disposition de l'appelant.

%%B sera toujours développé au format name = valeur à cause du commutateur /format:list passé à wmic; le nom sera le même que celui spécifié avec le verbe get, et c'est ainsi que le nom que vous transmettez finit par choisir la variable que vous récupérez. L'ensemble de l'argument name = value de set est cité au cas où l'attribut demandé contient des caractères spéciaux du shell. Cela rend: getfattr lui-même sûr, mais vous voudrez peut-être utiliser! Différé! expansion plutôt que% prématurée% où que vous utilisiez réellement la variable qu’elle vous remet.

Le & goto :eof sur cette même ligne rompt les deux boucles for et retourne à l'appelant de: getfattr dès que la boucle intérieure fait réellement quoi que ce soit, juste au cas où vous transmettriez un nom étrange et que wmic get produirait plus d'une ligne non vierge.

Les trois dernières lignes ne sont exécutées que si wmic ne produit aucune sortie, ce qui se produit en cas d'échec.

3
flabdablet

Ceci est mon fichier bat 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

Vous semblez avoir un ensemble supplémentaire de citations autour de la commande entière

Un problème avec les boucles for est qu’il dépasse une ligne vierge après la 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

Mettez-le dans un fichier. Bien que le fichier comporte deux lignes vides.

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

Peut-être que cela pourrait fonctionner

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

Ou appelez un autre fichier de commandes et ne revenez pas.

Voici un moyen de récupérer des lignes vierges.

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
Noodles

Voici une méthode alternative, contourne WMIC pour 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

Renvoie ce résultat lorsque vous double-cliquez sur un fichier de commandes .cmd.

Version
-------
0.0.0.0
0
Knuckle-Dragger

et deux manières sans outils externes 1.WMIC

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

Faites attention aux doubles barres obliques du nom du fichier.

2.MAKECABle WMIC n'étant pas installé sur les versions domestiques de Windows, voici un moyen d'utiliser makecab qui fonctionnera sur toutes les machines 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*"

exemple de sortie (il a une version chaîne qui est un petit ajout à la méthode 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 Utilisation de Shell.application et lot hybride\jscript.Here's 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();

utilisé contre 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
npocmaka