René Nyffenegger
René Nyffenegger

Reputation: 40499

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.

Upvotes: 2

Views: 488

Answers (2)

vonPryz
vonPryz

Reputation: 24071

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.

Upvotes: 1

stackprotector
stackprotector

Reputation: 13451

The -Path parameter of Select-String expects paths to files:

-Path

Specifies the path to the files to search. Wildcards are permitted. The default location is the local directory.

Specify files in the directory, such as log1.txt, *.doc, or .. If you specify only a directory, the command fails.

That's why you get an error, when you pass a path to a directory to the -Path parameter.

When you pipe objects to Select-String, they do not necessarily need a Path attribute that can be mapped to -Path of Select-String:

You can pipe any object that has a ToString method to Select-String.

So you can also pipe raw strings to Select-String:

'test' | Select-String -Pattern 'test'

This line will return test.

Get-ChildItem returns objects of type System.IO.FileInfo or System.IO.DirectoryInfo. Select-String will use the Path attribute of System.IO.FileInfo to parse the content of the file and it will use the string representation of System.IO.DirectoryInfo to parse exactly this string representation (the path itsself). That's why you don't get errors in your pipeline.

New-Item -Name 'test' -Type Directory | Select-String -Pattern 'test'

This line will return the path to the just created directory test.

Upvotes: 1

Related Questions