Find the version of a installed program from batch file
You have a set of misplaced double quotes, as well as an extra (
.
WMIC uses SQL syntax, and strings are enclosed in single quotes.The internal single quotes do not interfere with the command enclosing single quotes.
You can put double quotes around the WHERE clause (not including the WHERE keyword) to avoid some escape issues within 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 quite be the whole solution. You can't see it with the above code, but RESULT actually contains a trailing carriage return (0x0D). This is due to a quirk with how FOR /F handles WMIC unicode output. Every line of WMIC output will have the extra trailing carriage return.
As long as you always access RESULT using %RESULT%
(normal expansion), then you will not have any problems. But if you should ever need delayed expansion, then you can have problems, as demonstrated 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 method to strip the unwanted carriage return is to use an extra level of 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
Here's the subroutine I use for this in my own software update batch 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 might get the file version for the currently installed Adobe Reader:
call :getfattr version "%ProgramFiles(x86)%\Adobe\Reader 11.0\Reader\AcroRd32.exe"
echo "!version!"
After doing that, environment variable version
will contain the requested version string. If :getfattr
fails, version
is guaranteed to be unset.
A test execution trace for that example looks like this (delayed expansion was already enabled, though this is not assumed by :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, it's pretty direct and doesn't faff about too much. It does, however, tiptoe through a minefield of cmd
and wmic
gotchas.
First, the name of the attribute you want to get is also the name used for the variable you want the result to end up in (version
in the test above). Inside the subroutine, that name is %1
, so set %1=
clears it.
The filename you pass in needs a bit of preprocessing before it can be safely handed to wmic
and a shell variable is required for that, so setlocal
is issued to avoid stomping the caller's variables.
set "name=%~f2"
copies the name to an environment variable after stripping off any surrounding double-quotes and expanding it to a full pathname. Double quotes surround the entire set
argument to prevent grief caused by ampersands or parentheses in pathnames.
wmic
queries use a SQL-like syntax, where string values are surrounded by single quote '
characters and \
is an escape that suppresses any special meaning of the following character. Since both of these are legal in Windows pathnames, all occurrences of either need a \
prefix. set "name=%name:\=\\%"
escapes embedded backslashes, and the '%name:'=\'%'
construct in the wmic
command line escapes embedded single quotes and adds the required surrounding ones.
cmd
's parser doesn't turn off special processing between single quotes, and the name no longer has any surrounding double quotes, so embedded spaces, parentheses or ampersands could potentially break things. To guard against that, wmic
's entire name=
argument gets double quoted. There's no need for special handling for double quotes already inside the name, because double quotes are prohibited in Windows filenames so there can't be any.
The for
command line containing the wmic
command ends with a @^
sequence. The ^
serves to attach the next line as the payload of the outer for
command; the @
prevents that payload being echoed in an execution trace even if ECHO is on.
That echo suppression is done mainly because the inner for
exists only to get rid of the spurious CR characters injected by cmd
's buggy conversion of wmic
's output from Unicode to ASCII (the same technique used in @dbenham's answer) and if it's allowed to echo, those CRs just filthy up the trace with confusing overwrites. As a side benefit, the inner for
won't execute its own payload when the line it's handed from the outer for
contains only a CR, a version-dependent number of which wmic
insists on emitting. The inner for
's payload does get echoed if ECHO is on, so tracing still captures all the useful happenings.
That payload consists of three &-separated commands, which for
will expand as a single command line before cmd
gets to process the individual commands. In particular, this means that set "%%B"
gets expanded before endlocal
runs, which puts the variable created by that set
outside the setlocal
/endlocal
scope and makes it available to the caller.
%%B
will always expand in the format name=value because of the /format:list
switch passed to wmic
; the name will be the same as that specified with the get
verb, and this is how the name you pass in ends up choosing the variable you get back. The entire name=value argument to set
is quoted in case the requested attribute contains shell-special characters. This makes :getfattr itself safe, but you might want to use !delayed! expansion rather than %premature% expansion wherever you actually use the variable it hands back to you.
The & goto :eof
on that same line breaks from both for
loops and returns to :getfattr's caller as soon as the inner one actually does anything, just in case you pass in some weird name and wmic get
produces more than one non-blank line of output.
The last three lines only ever run if wmic
produces no output at all, which is what happens when it fails.