Lost Crotchet
Lost Crotchet

Reputation: 1400

How to list contents of matched directories whether or not Get-ChildItem string input contains wildcards or not in PowerShelll

I am looking for a way to consistently list directory contents in the following scenario i.e.

$dirs = @("C:/dir1", "C:/dir2", "C:/dir[3-5]")

foreach ($d in $dirs) {
  Get-ChildItem $d
}

For the first and second iterations the directory contents get listed, but for the 3rd iteration only the matching directories get listed rather than their contents.

Please provide for PowerShell v2+

Upvotes: 1

Views: 278

Answers (3)

mklement0
mklement0

Reputation: 437197

Note:

  • Don Cruickshank's helpful answer provides an effective and elegant solution.

  • This answer provides some background information and offers an alternative solution based on Convert-Path.


Indeed, a directory path specified as a wildcard expression causes the matching directories themselves rather than their content to be listed:

Literal path:

# NO wildcard -> directory *content* is listed
PS> Get-ChildItem C:\Windows

    Directory: C:\Windows


Mode                LastWriteTime         Length Name                                                                                                          
----                -------------         ------ ----                                                                                                          
d-----        4/11/2018   7:38 PM                addins                                                                                                        
d-----        8/16/2018  10:06 AM                appcompat                                                                                                     
d-----        10/9/2018   5:39 PM                apppatch                                                                                                      

Wildcard path (note the [...] around s):

# Wildcard -> resolved directory *itself* is listed
PS> Get-ChildItem C:\Window[s]


    Directory: C:\


Mode                LastWriteTime         Length Name                                                                                                          
----                -------------         ------ ----                                                                                                          
d-----        9/18/2018  10:07 PM                Windows                                                                                                       

Therefore, in order to list directories' contents, wildcard expressions must be resolved to their matching paths, before passing them to Get-ChildItem.

One option is to use the Convert-Path cmdlet first[1], and pass its output to Get-ChildItem via the -LiteralPath parameter:

Get-ChildItem -LiteralPath ('C:/dir1', 'C:/dir2' + (Convert-Path 'C:/dir[3-5]'))

Note: Using -LiteralPath isn't strictly needed, but good practice, because specifying paths positionally - as the first positional argument - implicitly binds to the -Path parameter, which interprets its arguments as wildcard patterns, and there is a risk of literal paths accidentally looking like wildcard patterns.

Note how the above command passes all (resulting) directories as an array to a single invocation of Get-ChildItem which makes the command more efficient.

However, in a given scenario you may still need to use individual Get-ChildItem calls in a loop:

foreach ($dir in 'C:/dir1', 'C:/dir2' + (Convert-Path 'C:/dir[3-5]')) {
   Get-ChildItem -LiteralPath $dir
}

Note the use of + - array concatenation, in this case - to ensure that a flat array of paths is created; by contrast, 'C:/dir1', 'C:/dir2', (Convert-Path 'C:/dir[3-5]) would create a nested array, with the wildcard-resolved paths all stored in a sub-array stored in the output array's last element.
Given that Get-ChildItem -LiteralPath also accepts arrays, this wouldn't cause an error, but it wouldn't result in one-by-one dir. processing.


[1] There's also a Resolve-Path cmdlet, but it returns [System.Management.Automation.PathInfo] instances, which aren't strictly needed here, since it is strings that bind to -LiteralPath.
Also, Convert-Path always returns an actual filesystem path, whereas Resolve-Path resolves to PS drive-based paths, if applicable - while that won't make a difference here, it would if you passed the paths to .NET methods, for instance.

Upvotes: 3

Don Cruickshank
Don Cruickshank

Reputation: 5938

You could use Get-Item to perform the wildcard expansion and then get the child items of each match:

foreach ($dir in Get-Item $dirs) {
    Get-ChildItem -LiteralPath $dir
}

Upvotes: 2

TobyU
TobyU

Reputation: 3908

Use Get-ChildItem -LiteralPath $d instead of Get-ChildItem $d since the first pipline variable will be used as the -Path which allows wildcard expressions. -LiteralPath $d does not.

$dirs = @("C:/dir1", "C:/dir2", "C:/dir[3-5]")

foreach ($d in $dirs) {
  Get-ChildItem -LiteralPath $d
}

Upvotes: 0

Related Questions