Skip to content

Instantly share code, notes, and snippets.

@rbleattler
Last active April 16, 2021 12:22
Show Gist options
  • Save rbleattler/8d019bb8ad4783a6c1041f5380344358 to your computer and use it in GitHub Desktop.
Save rbleattler/8d019bb8ad4783a6c1041f5380344358 to your computer and use it in GitHub Desktop.
Finds String(s) in any non-excluded file (text-readable) in the target directory. Supports recursion. !MAY BE BROKEN!
function Find-StringInFile {
<#
.SYNOPSIS
Find strings within the contents of files in a given directory.
.DESCRIPTION
Find strings within the contents of files in a given directory. Supports recursion
.EXAMPLE
PS C:\> Find-StringInFile -String "SomeExampleText" -RootPath $PWD
This will search the current directory for files containing the string "SomeExampleText"
.EXAMPLE
PS C:\> Find-StringInFile -String "SomeExampleText" -RootPath $PWD -Recurse
This will search the current directory, and all subdirectories for files containing the string "SomeExampleText"
.INPUTS
String
.OUTPUTS
[System.Collections.Generic.List[Object]]
.NOTES
Useful in Windows Filesystems where searching in file explorer is slow and arduous.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory)]
[string[]]
$String,
[Parameter()]
[string]
$RootPath = $PWD,
[Parameter()]
[switch]
$Recurse,
[Parameter()]
[String[]]
$Excludes,
[Parameter(DontShow)]
[switch]
$Transcript,
[Parameter(DontShow)]
[String]
$LogPath
)
begin {
Write-Debug "Enter [$($PSCmdlet.MyInvocation.MyCommand.Name)]..."
$PSBoundParameters.Keys | ForEach-Object {
if ($PSBoundParameters.$PSItem -is [string]) {
Write-Debug "[$($PSCmdlet.MyInvocation.MyCommand.Name)] $_ : $($PSBoundParameters.Item($_))"
} else {
Write-Debug "[$($PSCmdlet.MyInvocation.MyCommand.Name)] $_ : $($PSBoundParameters.Item($_).GetType())"
}
}
if ($Transcript) {
if ([string]::IsNullOrWhiteSpace($LogPath)) {
$LogPath = $ENV:Temp
}
$ScriptName = $PSCmdlet.MyInvocation.MyCommand.Name.Split('.ps')[0]
$Now = Get-Date -Format yyyyMMdd_hh-mm-ss
$ScriptLogName = '{0}.{1}.log' -f $Now, $ScriptName
$ScriptLog = Join-Path -Path $LogPath -ChildPath $ScriptLogName
Start-Transcript -Path $ScriptLog
}
if ($False -eq [System.IO.Directory]::Exists($RootPath)) {
Write-Warning -Message "Could not resolve $RootPath"
}
$Excludes = $Excludes += "*.exe"
$TotalCount = $String.Count
$Parameters = @{
Exclude = $Excludes
File = $true
}
if ($Recurse) {
$Parameters.Recurse = $true
}
$ChildItems = Get-ChildItem @Parameters
$TotalFiles = $($ChildItems.Count)
Write-Host "Processing $TotalCount string on $TotalFiles files..."
$ChildItemObjects = [System.Collections.Concurrent.BlockingCollection[Object]]::new()
}
process {
$ProgressRecord = [System.Management.Automation.ProgressRecord]::new(0, 'Checking', "...")
$ChildItems | ForEach-Object -Parallel {
$OutHost = $Using:Host
$OutSideRecord = $Using:ProgressRecord
$OutSideRecord.CurrentOperation = "$($PSItem.Name)..."
$OutHost.UI.WriteProgress( 0, $OutSideRecord)
$List = $Using:ChildItemObjects
$Item = [pscustomobject]@{
FileName = $PSItem.FullName;
Contents = [System.IO.File]::ReadAllText("$($PSItem.FullName)")
Match = $false
}
if ($Item.Contents -like "*$Using:String*") {
$List.Add($Item)
}
} -ThrottleLimit $([int]$env:NUMBER_OF_PROCESSORS - 1) -Verbose
}
end {
Write-Host 'DONE!'
Write-Verbose -Message "Found $($ChildItemObjects.Count) Instances of any input strings found in search scope..."
$ChildItemObjects
if ($Transcript) {
Stop-Transcript
}
Write-Debug "Exit [$($PSCmdlet.MyInvocation.MyCommand.Name)]..."
}
}
function Find-StringInFile {
<#
.SYNOPSIS
Find strings within the contents of files in a given directory.
.DESCRIPTION
Find strings within the contents of files in a given directory. Supports recursion
.EXAMPLE
PS C:\> Find-StringInFile -String "SomeExampleText" -RootPath $PWD
This will search the current directory for files containing the string "SomeExampleText"
.EXAMPLE
PS C:\> Find-StringInFile -String "SomeExampleText" -RootPath $PWD -Recurse
This will search the current directory, and all subdirectories for files containing the string "SomeExampleText"
.INPUTS
String
.OUTPUTS
[System.Collections.Generic.List[Object]]
.NOTES
Useful in Windows Filesystems where searching in file explorer is slow and arduous.
#>
[CmdletBinding()]
param (
[Parameter(Mandatory, HelpMessage = "Enter the string you're searching for.", Position = 0)]
[string[]]
$String,
[Parameter(HelpMessage = "Enter the path to the directory you'd like to search. Defaults to current directory.", Position = 1)]
[string]
$RootPath = $PWD,
[Parameter(HelpMessage = "If enabled, the search will also search all subdirectories below the directory defined in `$RootPath")]
[switch]
$Recurse,
[Parameter(HelpMessage = "Enter filters to exclude. (Executables and media files are filtered out by default)")]
[String[]]
$Exclude
)
begin {
$Exclude = $Exclude += "*.exe", "*.*z*", "*.*rar*", "*.PNG", "*.JPG", "*.JPEG", "*.BMP", "*.GIF",
"*.WAV", "*.MID", "*.MIDI", "*.WMA", "*.MP3", "*.OGG", "*.RMA",
"*.AVI", "*.MP4", "*.DIVX", "*.WMV"
$TotalCount = $String.Count
$ChildItems = Get-ChildItem -LiteralPath $RootPath -Exclude $Exclude -File -Recurse:$Recurse
$TotalFiles = $($ChildItems.Count)
Write-Host "Processing $TotalCount string on $TotalFiles files..."
$ChildItemObjects = New-Object -TypeName System.Collections.Generic.List[Object]
}
Process {
$ChildItems | ForEach-Object {
$Message = "Checking $($PSItem.Name)..."
Write-Verbose -Message $Message
$Item = [pscustomobject]@{
FileName = $PSItem.FullName;
Contents = Get-Content -Path $PSItem.FullName -Raw
Match = $false
}
if ($Item.Contents -like "*$String*") {
$ChildItemObjects.Add($Item)
}
}
}
end {
Write-Host 'DONE!'
$ChildItemObjects
Write-Verbose -Message "Found $($ChildItemObjects.Count) Instances of any input strings found in search scope..."
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment