Filtering files by their metadata (extended properties)


When you run Get-ChildItem or any of its’ aliases, you’ll get back an array of objects, usually System.IO.FileInfo and System.IO.DirectoryInfo (if you have directories too).

The properties displayed are Mode, LastWritetime, Length and Name. Of course there are other properties in the returned objects, but these are the ones that get displayed by default (more on what gets displayed by default and why, in a future post).

You can use the Format-* or the Select-Object cmdlets to output the other properties and their values, or run Get-ChildItem | Get-Member -MemberType Properties to list all the properties in the PowerShell object.

PS C:\> Get-ChildItem | Get-Member -MemberType Properties
 
 
   TypeName: System.IO.FileInfo
 
Name              MemberType     Definition                                                                      
----              ----------     ----------                                                                      
Mode              CodeProperty   System.String Mode{get=Mode;}                                                   
PSChildName       NoteProperty   System.String PSChildName=myFile.txt                                                 
PSDrive           NoteProperty   System.Management.Automation.PSDriveInfo PSDrive=C                              
PSIsContainer     NoteProperty   System.Boolean PSIsContainer=False                                              
PSParentPath      NoteProperty   System.String PSParentPath=Microsoft.PowerShell.Core\FileSystem::C:\            
PSPath            NoteProperty   System.String PSPath=Microsoft.PowerShell.Core\FileSystem::C:\myFile.txt             
PSProvider        NoteProperty   System.Management.Automation.ProviderInfo PSProvider=Microsoft.PowerShell.Core...
Attributes        Property       System.IO.FileAttributes Attributes {get;set;}                                  
CreationTime      Property       datetime CreationTime {get;set;}                                                
CreationTimeUtc   Property       datetime CreationTimeUtc {get;set;}                                             
Directory         Property       System.IO.DirectoryInfo Directory {get;}                                        
DirectoryName     Property       string DirectoryName {get;}                                                     
Exists            Property       bool Exists {get;}                                                              
Extension         Property       string Extension {get;}                                                         
FullName          Property       string FullName {get;}                                                          
IsReadOnly        Property       bool IsReadOnly {get;set;}                                                      
LastAccessTime    Property       datetime LastAccessTime {get;set;}                                              
LastAccessTimeUtc Property       datetime LastAccessTimeUtc {get;set;}                                           
LastWriteTime     Property       datetime LastWriteTime {get;set;}                                               
LastWriteTimeUtc  Property       datetime LastWriteTimeUtc {get;set;}                                            
Length            Property       long Length {get;}                                                              
Name              Property       string Name {get;}                                                              
BaseName          ScriptProperty System.Object BaseName {get=if ($this.Extension.Length -gt 0){$this.Name.Remov...
VersionInfo       ScriptProperty System.Object VersionInfo {get=[System.Diagnostics.FileVersionInfo]::GetVersio...

 

But when you use the Windows explorer in details view, you can see that the files contain a lot of other properties (such as Publisher, Rating, Bit rate, Genre, etc. for music files and Camera model, Date taken, Dimensions, Exposure time, etc. for pictures), these are called extended properties.

If you want to list these extended properties in PowerShell, or filter the files in a folder by a value on one of those properties you will have to use the Shell.Application ComObject.

For this purpose exactly, I wrote my own version of the Get-FileMetaData function (based on Ed Wilson’s version described in http://blogs.technet.com/b/heyscriptingguy/archive/2014/02/06/use-powershell-to-find-metadata-from-photograph-files.aspx):

function Get-FileMetaData {

 
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true,
                   ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true)]
        [Alias('FullName', 'PSPath')]
        [string[]]$Path
    )
 
    begin {
        $oShell = New-Object -ComObject Shell.Application
    }
 
    process {
        $Path | ForEach-Object {
 
            if (Test-Path -Path $_ -PathType Leaf) {
 
                $FileItem = Get-Item -Path $_
 
                $oFolder = $oShell.Namespace($FileItem.DirectoryName)
                $oItem = $oFolder.ParseName($FileItem.Name)
 
                $props = @{}
 
                0..287 | ForEach-Object {
                    $ExtPropName = $oFolder.GetDetailsOf($oFolder.Items, $_)
                    $ExtValName = $oFolder.GetDetailsOf($oItem, $_)
               
                    if (-not $props.ContainsKey($ExtPropName) -and
                            ($ExtPropName -ne '')) {
                                $props.Add($ExtPropName, $ExtValName)
                    }
 
                }
 
                New-Object PSObject -Property $props
            }
        }
 
    }
 
    end {
        $oShell = $null
    }
}
 

And you can use it, for example:

dir -Path C:\Pictures | Get-FileMetaData | Where-Object { $_.'Camera maker' -eq 'NIKON' }

or:

dir -Path C:\Music -Recurse | Get-FileMetaData | Where-Object { $_.Genre -in @('Rock','Pop') } | Select-Object Path, Genre, 'Bit rate'

 

 

Thanks to Israel Gofman for the PowerShell challenge.

HTH,

\Martin.

Comments (5)

  1. ankor says:

    Hi, Great article. I’m trying to adapt this to access Rating with music files. I’ve tried ……{ $_.’Rating’ -eq ‘3’ } but the filtering based on Rating is not happening. Is the phrase correct for filtering on Rating? Thanks in advance.

    1. Hi ankor,
      The possible values for the Rating are ‘1 Star’, ‘2 Stars’, ‘3 Stars’, ‘4 Stars’, ‘5 Stars’ and ‘Unrated’
      So you could try something like:
      dir -Path 'C:\TestMusic' | Get-FileMetaData | Where-Object { $_.Rating -eq '2 Stars' } | Select-Object Path, Rating

  2. ankor says:

    Thanks so much for the reply. I’ve tried it but nothing works. My background is programming but a complete noob to PowerShell. I’m actually wanting to copy files to a folder so I’ve tried…

    Copy-Item “G:\musica\MSpecial\*.*” -Destination “G:\temp” | Get-FileMetaData | Where-Object { $_.’Rating’ -eq ‘2 Stars’ }
    Copy-Item “G:\musica\MSpecial\*.*” -Destination “G:\temp” | Get-FileMetaData | Where-Object { $_.Rating -eq ‘2 Stars’ }
    Copy-Item “G:\musica\MSpecial\*.*” -Destination “G:\temp” | Get-FileMetaData | Where-Object { $_.Rating -Match “2” } | Select-Object Path, Rating
    Copy-Item “G:\musica\MSpecial\*.*” -Destination “G:\temp” | Get-FileMetaData | Where-Object { $_.Rating -Like “2*” } | Select-Object Path, Rating

    No matter what I do, all files get copied to the destination folder. I was surprised and baffled when -Match and -Like didn’t help.

    BTW, I’m using PowerShell ISE and when I try “dir” it displays nothing. I was expecting a listing like you’d get with “dir” in cmd prompt window. Where does it go?

    I’m looking at file properties in explorer and not seeing the extended info I want to use. I use MusicBee to manage music files and it supports Rating in half-steps. I need to do some checking with them about the metadata too. Could be that everything is “unrated” but that should have been reflected using Match and Like. Strange?

    One other thing, since I’m only interested in Rating, how could the Get-FileMetaData function be simplified (and faster) to just grab Rating and not loop through all properties?

    Thank you so very much for the help.

  3. ankor says:

    For just Rating & Genre, would this be right?

    function Get-FileMetaData {
    [CmdletBinding()]
    param(
    [Parameter(Mandatory = $true,
    ValueFromPipeline = $true,
    ValueFromPipelineByPropertyName = $true)]
    [Alias(‘FullName’, ‘PSPath’)]
    [string[]]$Path
    )
    begin {
    $oShell = New-Object -ComObject Shell.Application
    }
    process {
    $Path | ForEach-Object {
    if (Test-Path -Path $_ -PathType Leaf) {
    $FileItem = Get-Item -Path $_
    $oFolder = $oShell.Namespace($FileItem.DirectoryName)
    $oItem = $oFolder.ParseName($FileItem.Name)
    $FileMetaData = New-Object PSObject
    $props = @{}

    $ExtPropName = $oFolder.GetDetailsOf($oFolder.Items, $Rating)
    $ExtValName = $oFolder.GetDetailsOf($oItem, $Rating)
    if (-not $props.ContainsKey($ExtPropName) -and
    ($ExtPropName -ne ””)) {
    $props.Add($ExtPropName, $ExtValName)
    }
    $ExtPropName = $oFolder.GetDetailsOf($oFolder.Items, $Genre)
    $ExtValName = $oFolder.GetDetailsOf($oItem, $Genre)
    if (-not $props.ContainsKey($ExtPropName) -and
    ($ExtPropName -ne ””)) {
    $props.Add($ExtPropName, $ExtValName)
    }

    New-Object PSObject -Property $props
    }
    }
    }
    end {
    $oShell = $null
    }
    }

Skip to main content