Find-InFiles.psm1

function Find-InFiles {
<#
.SYNOPSIS
    Find in files.
.DESCRIPTION
    Finds a string within files of a specific type within the given locations and/or files with names containing that string.
.PARAMETER Needle
    <string to find>
.PARAMETER SubNeedle
    <optional second string to find within files that match the primary Needle pattern. When specified, files must contain both strings to be considered a match>
.PARAMETER Haystacks
    <paths to check>
.PARAMETER FileTypes
    <types of files to search through>
.PARAMETER MaxAge
    <maximum age in days>
.PARAMETER Quiet
    <do not output results to console>
.EXAMPLE
    Find-InFiles 'waldo'
.EXAMPLE
    Find-InFiles -Haystacks $($env:PSModulePath + ',' -replace(';', ',') -replace(',', '\%,') -replace('\\\\%', '\%') | Sort-Object -Unique) -FileTypes '*.ps1,*.psm1' -Needle 'waldo' -NameMatch
.EXAMPLE
    Find-InFiles -Haystacks "$($env:UserProfile)\Source\%" -FileTypes '*.ps1,*.psm1,*.cmd,*.bat,*.sql' -Needle 'waldo'
.EXAMPLE
    Find-InFiles -Needle 'error' -SubNeedle 'critical' -FileTypes '*.log'
.NOTES
    20170117 NomadicDaddy Initial release.
    20170224 NomadicDaddy Added recursion per haystack (append % to haystack to recurse).
    20170325 NomadicDaddy Added NameMatch.
    20180814 NomadicDaddy Added MaxAge.
    20180909 NomadicDaddy Converted to function/module.
    20180910 NomadicDaddy Returning an object would be more helpful. Doh!
    20241129 NomadicDaddy Added SubNeedle.
#>

[CmdletBinding(SupportsShouldProcess = $false, PositionalBinding = $false, ConfirmImpact = 'Low')]
Param(
    [Parameter(Mandatory = $true, ValueFromPipeLine = $true, ValueFromPipeLineByPropertyName = $true, Position = 0)]
        [string]$Needle,
    [Parameter(Mandatory = $false, Position = 1)]
        [string]$Haystacks = ".\%",
    [Parameter(Mandatory = $false, Position = 2)]
        [string]$FileTypes = '*.bat,*.cmd,*.ps1,*.psm1,*.sql',
    [Parameter(Mandatory = $false, Position = 3)]
        [switch]$NameMatch,
    [Parameter(Mandatory = $false, Position = 4)]
        [int]$MaxAge = 0,
    [Parameter(Mandatory = $false, Position = 5)]
        [string]$SubNeedle,
    [Parameter(Mandatory = $false, Position = 6)]
        [switch]$Quiet
)

$HeapArray = @()

foreach ($Haystack in ($Haystacks -split ',')) {
    $Recurse = $false
    $hl = $Haystack.Length
    ($Haystack, $Recurse) = $Haystack -split '%'
    if ($hl -ne $Haystack.Length) {
        $Recurse = $true
    }
    if (-not $Quiet) {
        Write-Host ("`r`nLOOKING FOR '{0}' IN: {1} {2} $(if ($Recurse) { ' (RECURSIVE)' } else { '' })" -f $Needle, $Haystack, $FileTypes) -ForegroundColor 'White'
    }
    foreach ($FileType in ($FileTypes -split ',')) {
        Get-ChildItem -Path $Haystack -Filter $FileType -Recurse:$Recurse -File |
            ForEach-Object {
                try {
                    $content = (Get-Content -Path $_.FullName)
                } catch {
                    $content = ''
                }
                if (($content | Select-String -Pattern $Needle) -and ($SubNeedle -eq '' -or ($content | Select-String -Pattern $SubNeedle))) {
                    if (-not $Quiet) {
                        Write-Host "+ "  -ForegroundColor 'Green' -NoNewLine
                        Write-Host ("{0,22} " -f $_.LastWriteTime) -ForegroundColor 'Green' -NoNewLine
                        Write-Host $_.FullName -ForegroundColor 'Green'
                    }
                    $HeapArray += $_
                } elseif ($NameMatch -and $_.FullName -imatch $Needle) {
                    if (-not $Quiet) {
                        Write-Host "+ "  -ForegroundColor 'Cyan' -NoNewLine
                        Write-Host ("{0,22} " -f $_.LastWriteTime) -ForegroundColor 'Cyan' -NoNewLine
                        Write-Host $_.FullName -ForegroundColor 'Cyan'
                    }
                    $HeapArray += $_
                } else {
                    if (-not $Quiet) {
                        Write-Host "+ "  -ForegroundColor 'Gray' -NoNewLine
                        Write-Host ("{0,22} " -f $_.LastWriteTime) -ForegroundColor 'DarkGray' -NoNewLine
                        Write-Host $_.FullName -ForegroundColor 'Gray'
                    }
                }
            }
    }
}

if (-not $Quiet) {

    if ($NameMatch -eq $true) {
        Write-Host ("`r`n{0} files containing or named like '{1}':`r`n" -f $HeapArray.Count, $Needle) -ForegroundColor 'White'
    } else {
        if ($SubNeedle) {
            Write-Host ("`r`n{0} files containing both '{1}' and '{2}':`r`n" -f $HeapArray.Count, $Needle, $SubNeedle) -ForegroundColor 'White'
        } else {
            Write-Host ("`r`n{0} files containing '{1}':`r`n" -f $HeapArray.Count, $Needle) -ForegroundColor 'White'
        }
    }

    foreach ($file in $HeapArray) {
        Write-Host $file.FullName
    }

}

return $HeapArray

}

Set-Alias -Name 'fifi' -Value 'Find-InFiles'