Get file version in PowerShell - file

How can you get the version information from a .dll or .exe file in PowerShell?
I am specifically interested in File Version, though other version information (that is, Company, Language, Product Name, etc.) would be helpful as well.

Since PowerShell 5 in Windows 10, you can look at FileVersionRaw (or ProductVersionRaw) on the output of Get-Item or Get-ChildItem, like this:
(Get-Item C:\Windows\System32\Lsasrv.dll).VersionInfo.FileVersionRaw
It's actually the same ScriptProperty from my Update-TypeData in the original answer below, but built-in now.
In PowerShell 4, you could get the FileVersionInfo from Get-Item or Get-ChildItem, but it would show the original FileVersion from the shipped product, and not the updated version. For instance:
(Get-Item C:\Windows\System32\Lsasrv.dll).VersionInfo.FileVersion
Interestingly, you could get the updated (patched) ProductVersion by using this:
(Get-Command C:\Windows\System32\Lsasrv.dll).Version
The distinction I'm making between "original" and "patched" is basically due to the way the FileVersion is calculated (see the docs here). Basically ever since Vista, the Windows API GetFileVersionInfo is querying part of the version information from the language neutral file (exe/dll) and the non-fixed part from a language-specific mui file (which isn't updated every time the files change).
So with a file like lsasrv (which got replaced due to security problems in SSL/TLS/RDS in November 2014) the versions reported by these two commands (at least for a while after that date) were different, and the second one is the more "correct" version.
However, although it's correct in LSASrv, it's possible for the ProductVersion and FileVersion to be different (it's common, in fact). So the only way to get the updated Fileversion straight from the assembly file is to build it up yourself from the parts, something like this:
Get-Item C:\Windows\System32\Lsasrv.dll | ft FileName, File*Part
Or by pulling the data from this:
[System.Diagnostics.FileVersionInfo]::GetVersionInfo($this.FullName)
You can easily add this to all FileInfo objects by updating the TypeData in PowerShell:
Update-TypeData -TypeName System.IO.FileInfo -MemberName FileVersionRaw -MemberType ScriptProperty -Value {
[System.Diagnostics.FileVersionInfo]::GetVersionInfo($this.FullName) | % {
[Version](($_.FileMajorPart, $_.FileMinorPart, $_.FileBuildPart, $_.FilePrivatePart)-join".")
}
}
Now every time you do Get-ChildItem or Get-Item you'll have a FileVersionRaw property that shows the updated File Version ...

Since PowerShell can call .NET classes, you could do the following:
[System.Diagnostics.FileVersionInfo]::GetVersionInfo("somefilepath").FileVersion
Or as noted here on a list of files:
get-childitem * -include *.dll,*.exe | foreach-object { "{0}`t{1}" -f $_.Name, [System.Diagnostics.FileVersionInfo]::GetVersionInfo($_).FileVersion }
Or even nicer as a script: https://jtruher3.wordpress.com/2006/05/14/powershell-and-file-version-information/

'dir' is an alias for Get-ChildItem which will return back a System.IO.FileInfo class when you're calling it from the filesystem which has VersionInfo as a property. So ...
To get the version info of a single file do this:
PS C:\Windows> (dir .\write.exe).VersionInfo | fl
OriginalFilename : write
FileDescription : Windows Write
ProductName : Microsoft® Windows® Operating System
Comments :
CompanyName : Microsoft Corporation
FileName : C:\Windows\write.exe
FileVersion : 6.1.7600.16385 (win7_rtm.090713-1255)
ProductVersion : 6.1.7600.16385
IsDebug : False
IsPatched : False
IsPreRelease : False
IsPrivateBuild : False
IsSpecialBuild : False
Language : English (United States)
LegalCopyright : © Microsoft Corporation. All rights reserved.
LegalTrademarks :
PrivateBuild :
SpecialBuild :
For multiple files this:
PS C:\Windows> dir *.exe | %{ $_.VersionInfo }
ProductVersion FileVersion FileName
-------------- ----------- --------
6.1.7600.16385 6.1.7600.1638... C:\Windows\bfsvc.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\explorer.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\fveupdate.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\HelpPane.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\hh.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\notepad.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\regedit.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\splwow64.exe
1,7,0,0 1,7,0,0 C:\Windows\twunk_16.exe
1,7,1,0 1,7,1,0 C:\Windows\twunk_32.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\winhlp32.exe
6.1.7600.16385 6.1.7600.1638... C:\Windows\write.exe

I realise this has already been answered, but if anyone's interested in typing fewer characters, I believe this is the shortest way of writing this in PS v3+:
ls application.exe | % versioninfo
ls is an alias for Get-ChildItem
% is an alias for ForEach-Object
versioninfo here is a shorthand way of writing {$_.VersionInfo}
The benefit of using ls in this way is that you can easily adapt it to look for a given file within subfolders. For example, the following command will return version info for all files called application.exe within subfolders:
ls application.exe -r | % versioninfo
-r is an alias for -Recurse
You can further refine this by adding -ea silentlycontinue to ignore things like permission errors in folders you can't search:
ls application.exe -r -ea silentlycontinue | % versioninfo
-ea is an alias for -ErrorAction
Finally, if you are getting ellipses (...) in your results, you can append | fl to return the information in a different format. This returns much more detail, although formatted in a list, rather that on one line per result:
ls application.exe -r -ea silentlycontinue | % versioninfo | fl
fl is an alias for Format-List
I realise this is very similar to xcud's reply in that ls and dir are both aliases for Get-ChildItem. But I'm hoping my "shortest" method will help someone.
The final example could be written in long-hand in the following way:
Get-ChildItem -Filter application.exe -Recurse -ErrorAction SilentlyContinue | ForEach-Object {$_.VersionInfo} | Format-List
... but I think my way is cooler and, for some, easier to remember. (But mostly cooler).

I prefer to install the PowerShell Community Extensions and just use the Get-FileVersionInfo function that it provides.
Like so:
Get-FileVersionInfo MyAssembly.dll
with output like:
ProductVersion FileVersion FileName
-------------- ----------- --------
1.0.2907.18095 1.0.2907.18095 C:\Path\To\MyAssembly.dll
I've used it against an entire directory of assemblies with great success.

Just another way to do it is to use the built-in file access technique:
(get-item .\filename.exe).VersionInfo | FL
You can also get any particular property off the VersionInfo, thus:
(get-item .\filename.exe).VersionInfo.FileVersion
This is quite close to the dir technique.

This is based on the other answers, but is exactly what I was after:
(Get-Command C:\Path\YourFile.Dll).FileVersionInfo.FileVersion

[System.Diagnostics.FileVersionInfo]::GetVersionInfo("Path\To\File.dll")

I find this useful:
function Get-Version($filePath)
{
$name = #{Name="Name";Expression= {split-path -leaf $_.FileName}}
$path = #{Name="Path";Expression= {split-path $_.FileName}}
dir -recurse -path $filePath | % { if ($_.Name -match "(.*dll|.*exe)$") {$_.VersionInfo}} | select FileVersion, $name, $path
}

As EBGreen said, [System.Diagnostics.FileVersionInfo]::GetVersionInfo(path) will work, but remember that you can also get all the members of FileVersionInfo, for example:
[System.Diagnostics.FileVersionInfo]::GetVersionInfo(path).CompanyName
You should be able to use every member of FileVersionInfo documented here, which will get you basically anything you could ever want about the file.

Here an alternative method. It uses Get-WmiObject CIM_DATAFILE to select the version.
(Get-WmiObject -Class CIM_DataFile -Filter "Name='C:\\Windows\\explorer.exe'" | Select-Object Version).Version

Related

Search Directory for folders

I am trying to create a batch file that I can use to type in a name of a folder and search multiple directories, then display the results in a new window. Example: I want to search for "tcash" in 3 separate directories, ie; \vm-xa01\users, vm-xa02\users and vm-xa03\users. How can I do this?
The original question had Powershell tag, so the answer is Powershell. For cmd (batch) script, I'd strongly suggest you to move into Powershell anyway. It's 2018 and cmd scripts require lots of tweaking.
In Powershell, there's a built-in cmdlet Out-GridView that might be suitable. For example, to display all txt files in c:\some\path and its subdirectories requires just a few commands. Like so,
gci c:\some\path -Recurse | ? { $_.extension -eq ".txt" } | ogv
First off, get a recursive list of all files
gci c:\some\path -Recurse
Then select those that have extension .txt
| ? { $_.extension -eq ".txt" }
Finally, pass the results to out-gridview aka ogv
| ogv
I also think PowerShell is the better script language for your task.
You can do a dir/Get-ChildItem with ranges [1-3] similar to a Regular Expression, so:
Get-ChildItem "\vm-xa0[1-3]\users\tcash" -File -Recurse | Out-Gridview
should enumerate all matching files and display in a gui window.

Windows Powershell: Display File Path along with Name and LastWriteTime

I'm using a Windows Powershell code that is very good but with one caveat. See below:
PS F:\Bizfi> dir -r | Out-GridView | Select FullName, LastWriteTime
The caveat is that it does not display the file path. Is there a way to include the file path within the name or as a separate property?
Thanks
If you want to mimic normal Get-ChildItem output:
dir -r | Select Mode,LastWriteTime,Length,Name,Fullname | Out-GridView

Rename files in Powershell deleting everything after "_"

I am renaming several hundred files, all which vary with an item ID and then a bunch of text after. For example...
BBAT300_abcdefg.xls
BBAT400_abcdefg.xls
I need to delete everything from the underscore, and including the underscore on, so the result is this:
BBAT300.xls
BBAT400.xls
I found this bit of code earlier...
Get-ChildItem -Name -Filter *.xls | foreach {[Regex]::Match($_,"^[^_]*").Value}
and it appears to work, but I can't get it to actually rename the files. My knowledge of scripting is little to none, so this may be an easy fix, I just can't seem to find it. Powershell will show the results in powershell, but not actually rename the files.
You can just pipe this line to the rename item command:
Get-ChildItem -Filter *.txt | Foreach-Object -Process {
$NewName = [Regex]::Match($_.Name,"^[^_]*").Value + '.txt'
$_ | Rename-Item -NewName $NewName
}
EDIT: Changed it to support any location

Search a literal in a zip file

I have a folder with oracle bi publisher reports
Folder/Usage Trend Report.xdmz
Folder/Usage Summary Report.xdmz
Folder/Charge Trend Report.xdmz
Folder/Consolidation Reports.xdmz
Folder/Charge Summary Report.xdmz
Each report is like zip file and contains some files
_datamodel.xdm
~metadata.meta
security.xml
I want to make a batch file, searches all the _datamodel.xdm file in order to find a literal (eg INVOICE_NBR or invoice_nbr)
The output will be something like
Report Name Literal Usages
Consolidation Reports.xdmz INVOICE_NBR 1
Is there anyone to help me do it ?
Thanks
In the future, please show what you've tried to solve the problem on your own, and where you're getting stuck. For this time, I found the challenge interesting, so I whipped up a beginning for you. It's a Batch + PowerShell hybrid script. Save it with a .bat extension and salt to taste. Be advised that the regexp object that performs a count of strings uses case-sensitive matching; so "INVOICE_NBR" would not increment the count when searching for "invoice_nbr".
<# : batch portion
#echo off & setlocal
set "outerfile=*.xdmz"
set "innerfile=_datamodel.xdm"
set "search=invoice_nbr"
rem // re-launch self with PowerShell interpreter
powershell "iex (${%~f0} | out-string)"
goto :EOF
: end batch / begin PowerShell hybrid chimera #>
add-type -as System.IO.Compression.FileSystem
# // encapsulate loop into a collection for select | format-table
&{ foreach ($archive in (gci $env:outerfile)) {
# // create a temporary directory within %temp%
$tempdir = New-Item -path $env:temp -name ([Guid]::NewGuid()) -type dir
[IO.Compression.ZipFile]::ExtractToDirectory($archive, $tempdir)
# // For each innerfile found within the zip archive...
gci -path $tempdir -filter $env:innerfile -recurse | %{
new-object PSObject -property #{
"Report Name" = $archive.Name
"Usages" = ([regex]::Matches((gc $_.FullName | out-string), $env:search)).count
"Literal" = $env:search
}
}
Remove-Item $tempdir -recurse -force
} } | select "Report Name",Literal,Usages | format-table -auto
Example output:
Report Name Literal Usages
----------- ------- ------
Usage Summary Report.xdmz invoice_nbr 2
Usage Trend Report.xdmz invoice_nbr 2
If you want case-insensitive matching, add the following as a third argument to the [regex]::Matches() function:
[Text.RegularExpressions.RegexOptions]::IgnoreCase

Simple PowerShell LastWriteTime compare

I need a PowerShell script that can access a file's properties and discover the LastWriteTime property and compare it with the current date and return the date difference.
I have something like this...
$writedate = Get-ItemProperty -Path $source -Name LastWriteTime
...but I can not cast the LastWriteTime to a "DateTime" datatype. It says, "Cannot convert "#{LastWriteTime=...date...}" to "System.DateTime".
Try the following.
$d = [datetime](Get-ItemProperty -Path $source -Name LastWriteTime).lastwritetime
This is part of the item property weirdness. When you run Get-ItemProperty it does not return the value but instead the property. You have to use one more level of indirection to get to the value.
(ls $source).LastWriteTime
("ls", "dir", or "gci" are the default aliases for Get-ChildItem.)
I have an example I would like to share
$File = "C:\Foo.txt"
#retrieves the Systems current Date and Time in a DateTime Format
$today = Get-Date
#subtracts 12 hours from the date to ensure the file has been written to recently
$today = $today.AddHours(-12)
#gets the last time the $file was written in a DateTime Format
$lastWriteTime = (Get-Item $File).LastWriteTime
#If $File doesn't exist we will loop indefinetely until it does exist.
# also loops until the $File that exists was written to in the last twelve hours
while((!(Test-Path $File)) -or ($lastWriteTime -lt $today))
{
#if a file exists then the write time is wrong so update it
if (Test-Path $File)
{
$lastWriteTime = (Get-Item $File).LastWriteTime
}
#Sleep for 5 minutes
$time = Get-Date
Write-Host "Sleep" $time
Start-Sleep -s 300;
}
(Get-Item $source).LastWriteTime is my preferred way to do it.
I can't fault any of the answers here for the OP accepted one of them as resolving their problem. However, I found them flawed in one respect. When you output the result of the assignment to the variable, it contains numerous blank lines, not just the sought after answer. Example:
PS C:\brh> [datetime](Get-ItemProperty -Path .\deploy.ps1 -Name LastWriteTime).LastWriteTime
Friday, December 12, 2014 2:33:09 PM
PS C:\brh>
I'm a fan of two things in code, succinctness and correctness. brianary has the right of it for succinctness with a tip of the hat to Roger Lipscombe but both miss correctness due to the extra lines in the result. Here's what I think the OP was looking for since it's what got me over the finish line.
PS C:\brh> (ls .\deploy.ps1).LastWriteTime.DateTime
Friday, December 12, 2014 2:33:09 PM
PS C:\brh>
Note the lack of extra lines, only the one that PowerShell uses to separate prompts. Now this can be assigned to a variable for comparison or, as in my case, stored in a file for reading and comparison in a later session.
Slightly easier - use the new-timespan cmdlet, which creates a time interval from the current time.
ls | where-object {(new-timespan $_.LastWriteTime).days -ge 1}
shows all files not written to today.
Use
ls | % {(get-date) - $_.LastWriteTime }
It can work to retrieve the diff. You can replace ls with a single file.

Resources