Shell – Way to list and cat all files that contain string X in powershell

powershell

Currently, when I'm looking through a bunch of files for a string in powershell I use the following:

PS: C:\> ls -r | Select-String "dummy" | ls -r

And that lists all files that have that string in them. If I do either

PS: C:\> ls -r | Select-String "dummy" | ls -r | cat
PS: C:\> ls -r | Select-String "dummy" | cat

It will again search through and cat all the files that contain the string, but it all comes out in one chunk like

file 1 line 1
file 1 line 2
file 2 line 1
file 3 line 1
file 4 line 1

But there's no way to tell which line is in which file. Is there a way to have it list something like:

File 1.txt:
file1 line1
file2 line2

File 2.txt:
file2 line1

etc. A one liner would be preferred, but not necessary

Best Answer

One could argue this isn't a one-liner, but it should work in any case ("should" meaning I haven't tested it, but the script should be right):

Get-ChildItem -Recurse | Where-Object {(Select-String -InputObject $_ -Pattern 'dummy' -Quiet) -eq $true} | ForEach-Object {Write-Output $_; Get-Content $_}

Expanded, with comments:

# Get a listing of all files within this folder and its subfolders.
Get-ChildItem -Recurse |

# Filter files according to a script.
Where-Object {
    # Pick only the files that contain the string 'dummy'.
    # Note: The -Quiet parameter tells Select-String to only return a Boolean. This is preferred if you just need to use Select-String as part of a filter, and don't need the output.
    (Select-String -InputObject $_ -Pattern 'dummy' -Quiet) -eq $true
} |

# Run commands against each object found.
ForEach-Object {
    # Output the file properties.
    Write-Output $_;

    # Output the file's contents.
    Get-Content $_
}

And here's a "golfed" version, if you really want it short. This definitely comes closer to qualifying as a "one-liner".

ls -R|?{$_|Select-String 'dummy'}|%{$_;gc $_}

Aside from the obvious use of aliases, collapsing of whitespace, and truncation of parameter names, you may want to note the following significant differences between the "full" versions and the "golfed" version:

  • Select-String was swapped to use piped input instead of -InputObject.
  • The -Pattern parameter name was omitted from Select-String, as use of that parameter's name is optional.
  • The -Quiet option was dropped from Select-String. The filter will still work, but it will take longer since Select-String will process each complete file instead of stopping after the first matching line.
  • -eq $true was omitted from the filter rule. When a filter script already returns a Boolean, you do not need to add a comparison operator and object if you just want it to work when the Boolean is true.
    • (Also note that this will work for some non-Booleans, like in this script. Here, a match will result in a populated array object, which is treated as true, while a non-match will return an empty array which is treated as false.)
  • Write-Output was omitted. PowerShell will try to do this as a default action if an object is given without a command.

If you don't need all the file's properties, and just want the full path on one line before the file contents, you could use this instead:

ls -R|?{$_|Select-String 'dummy'}|%{$_.FullName;gc $_}
Related Question