Search code examples
powershellpipeline

Why does "get-childItem -recurse | select-string foo" not result in an error if there are subdirectories?


Trying to use select-string on a directory results in an error:

PS C:\> select-string pattern P:\ath\to\directory
select-string : The file P:\ath\to\directory cannot be read: Access to the path 'P:\ath\to\directory' is denied.

However, when I use get-childItem -recurse, the command finishes without problem:

PS C:\> get-childItem -recurse P:\ath\to | select-string pattern

This sursprises me because get-childItem recurse also passes directories down the pipeline. So, I'd have assumed that select-string raises the same error when it processes the first directory.

Since this is not the case, I wonder where or how the directory is filtered out in the pipeline.


Solution

  • TL;DR: object magic.

    When Get-Childitem is run, it will return a collection of objects. This is passed to Select-String. The cmdlet's source is available on Github. There's a ProcessRecord() method that, well, processes input objects. It contains a few checks for object type and if those are directories. Like so,

    if (_inputObject.BaseObject is FileInfo fileInfo)
    ...
    if (expandedPathsMaybeDirectory && Directory.Exists(filename))
    ...
    

    Thus, it doesn't matter that Get-Child's collection contains both DirectoryInfo and FileInfo objects; the cmdlet is smart enough to figure out what it's trying to consume.