PowerShell Basics #2: Dir for power users

You would think the standard dir command in cmd.exe would do its job adequately, but it’s still surprising how much more functionality can you pack into it. Here’s an introduction to power-using Get-ChildItem, also known by the aliases of dir and ls.

Leveraging FileInfo and DirectoryInfo

For starters, it is important to remember that dir returns objects. Namely, it returns objects of the abstract base type System.IO.FileSystemInfo. The concrete types are FileInfo and DirectoryInfo for files and directories, respectively. Most of the time you will not care about these type issues, though – both concrete types have the same key properties, and you can use them to filter the objects.

So, I can get all my PDC 2009 slides into an array variable by typing $decks = dir D:\Slides\Pdc2009.

Now which of those beasts exceed 10 megs in size?

PS D:\temp> $decks | where { $_.Length -gt 10MB }

    Directory: D:\slides\pdc2009

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        17.12.2009     12:49   19985726 VTL04 - Rx, Reactive Extensions for .NET.pptx

Umm, ok. Did any directories leak in?

PS D:\temp> $decks | where { $_ -is "System.IO.DirectoryInfo" }

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
d----        17.12.2009     13:34            curl
d----         28.1.2010     11:24            workshops

Now that we know the object model, let's look into what the various arguments in Get-ChildItem can actually do.

Multiple targets

The traditional dir was limited to listing one directory at a time. So much for getting a single list of all the files in your drives’ root directories. With PowerShell, this is no longer a problem. The –Path parameter (which you almost never refer by name, since it is the positionally first argument) takes an array of strings:

PS D:\temp> dir MyDir, MySecondDir

    Directory: D:\temp\MyDir

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         28.1.2010     11:39         22 Demo.txt

    Directory: D:\temp\MySecondDir

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---         28.1.2010     11:47      18588 Contents.html

Although these files are shown as two separate listings, it’s all just visual. If you do $files = dir MyDir, MySecondDir, you get back an array of FileSystemInfos (of two elements in the case above). The fact that the directory header gets printed into the list doesn’t mean they would be separated in the actual array – it’s just the output formatter that does the trick.

Recursion and filtering

Dir has always supported recursion, but supporting recursion and filtering side-by-side can sometimes be a bit troublesome. For example, the traditional dir had a /s switch for subdirectory recursion: dir C:\ /s listed all the files on your C drive.

It also allowed you a rudimentary approach to file filtering. You could do dir c:\windows\*.exe /s to list all the exe files under the Windows directory and its subdirectories, but the syntax was somewhat unintuitive. Behind the scenes, dir splits the path argument into two elements: where and what to search for (C:\Windows and *.exe, respectively). While handy, it makes certain scenarios hard.

For example, listing all the exe and zip files would require two listings. And what if you needed directory wildcarding, say “Find me all the jpg files in directories matching C:\Images\2008*”?

PowerShell resolves much of this by keeping the two concepts apart from another. With PowerShell, you  have –recurse (usually abbreviated as –r) for recursion, and the path argument specifies the locations you need to search files in. When you need filename filtering, you apply –include (or just –i), which takes an array of wildcard-enhanced masks.

So you want a list of all executable and zip files under your program files directories, even if you had a separate Program Files (x86) as you usually do on 64-bit systems? Do a dir –r "C:\Program Files*" –i *.exe, *.zip.

You also have the liberty of excluding stuff. Say, you didn't want exe and zip files that are actually installers? Throw in a -exclude setup.exe,installer.exe. Of course, feel free to abbreviate to -ex and use wildcards.

Filtering, more speed

The suggestion I gave above for using –include is sound advice, and works every time. However, there is one drawback to it: it can be a bit slow. Enter the –filter parameter, which takes a file mask (like *.exe), and performs the filtering at the file system level, producing the output far faster.

The reasons for this dual parameter set are beyond the scope of this article, but suffice to say that –filter is a faster but more limited option. In practice, you’ll probably encounter the fact that a filter only takes one filtering expression. If you need several, use the –include parameter. Also, you cannot represent an –exclude as a –filter.

A syntax shortcut helps in using -filter: it is the second positional parameter in a Get-ChildItem call. The first one is naturally the path. Therefore, you may omit the parameter name and simply type dir C:\Windows *.exe to get all the .exe files in your Windows directory. If you type dir C:\Windows\*.exe (notice the extra slash), you’re not passing a filter, but just one path. That’s fine, but usually a bit slower – it generally performs equally to specifying the file mask as an –include parameter.

Of course, you’re unlikely to spot the difference in practice unless you have really, really many files. When you do, the filter will often save the day.

Filtering with regular expressions

The Get-ChildItem cmdlet doesn’t support regular expressions, but you can use them by piping the resultant objects through an appropriate filtering expression. For example, in order to find out the slide decks that have a dual vowel (“aa”, “ee”, and so on) in their names, you might type:

dir d:\slides\pdc2009 | where { $_.Name -match '([aeiouy])\1' }

This would net you a set of presentations on IIS, toolkits and various deep dives.

Just the names?

In the cmd.exe times, we used to do dir /b to just print out the names of the files (/b stands for bare). The Get-ChildItem cmdlet has this one too, and it’s called –name (or just –n). While you’ll probably want to work with the objects when in PowerShell, interfacing with non-object-oriented tools often requires passing the path strings around.

There is one more thing here. –name produces a list of names relative to the root directory of the directory listing, which may contain paths like “System32\azroles.dll” for a directory listing that originated from the C:\Windows directory. While this is often fine, you may also need the full paths. For this, use pipelines to convert the objects into their full paths.

dir c:\windows –r | foreach { $_.FullName }

I think that’s about enough for one of the most basic commands, dir. Enjoy!

January 28, 2010 · Jouni Heikniemi · 2 Comments
Tags:  · Posted in: .NET, Windows IT

2 Responses

  1. Scripting STSADM with PowerShell | SharePoint Blues - April 28, 2010

    […] If you have no idea what I’m talking about, there are plenty of resources on TechNet, and some great tutorials by our CTO, Jouni Heikniemi to get you […]

  2. accounting hoodies - May 24, 2016

    The exhibition titled To Hell and Back, will feature a selection of lithographs (already in the museum's
    permanent collection) that takes on the epic poem The Divine Comedy, written by Dante Alighieri,
    and mixes in a 21st century narrative of a 'sneaker and hoodie-clad slacker', set in circles of Hell
    to Purgatory and Paradise, both presented as versions of modern American cities such as San Francisco and Los Angeles.
    Maybe then we may get some true answers as to who Sara really is.
    " Brady's post-season rep took a hit when the 18-0 New Englanders lost that '08 Super Bowl game to the New York Giants and the team proceeded to blow first-round playoff games in 2010 and 2011.

Leave a Reply