functions/Get-FileItem.ps1
Function Get-FileItem { [cmdletbinding(DefaultParameterSetName = "Default")] [OutputType("System.IO.FileInfo", "System.Boolean")] [alias("pswhere")] Param( [Parameter( Position = 0, Mandatory, HelpMessage = "Enter a filename or pattern to search for")] [ValidateNotNullOrEmpty()] [string[]]$Pattern, [switch]$Regex, [Parameter(ParameterSetName = "Path")] [string[]]$Path, [Parameter(ParameterSetName = "Path")] [switch]$Recurse, [switch]$Full, [switch]$Quiet, [switch]$First ) #region private helper function <# The Resolve-EnvVariable function is used by Get-FileItem to resolve any paths that might contain environmental names like %WINDIR% or %USERNAME% #> Function Resolve-EnvVariable { [cmdletbinding()] Param( [Parameter(Position = 0, Mandatory = $True, HelpMessage = "Enter a string that contains an environmental variable like %WINDIR%")] [ValidatePattern("%\S+%")] [string]$String ) Write-Verbose "Starting $($MyInvocation.MyCommand)" Write-Verbose "Resolving environmental variables in $String" [environment]::ExpandEnvironmentVariables($string) Write-Verbose "Ending $($MyInvocation.MyCommand)" } #end Resolve-EnvVariable function #endregion #This is the main part of Get-FileItem Write-Verbose "Starting $($MyInvocation.MyCommand)" Write-Verbose "Searching for $pattern" Write-Verbose "Quiet mode is $Quiet" Write-Verbose "Full mode is $Full" if ($path) { #use specified path or array of paths $paths = $path } else { #use %PATH% system environmental variable #split %PATH% and weed out any potential duplicates or null values $splitChar = [System.IO.Path]::PathSeparator $paths = $env:PATH.Split($splitChar) | Select-Object -Unique | Where-Object {$_} } #define a variable to hold results $results = @() #foreach path search for the pattern foreach ($path in $paths) { #if path has an environmental variable, resolve it first if ($path.Contains("%")) { Write-Verbose "Resolving environmental variables found in the path" $path = Resolve-EnvVariable -string $path } #Validate path is still good Write-Verbose "Testing $path" If (Test-Path -Path $path) { Write-Verbose "Searching $path" #search for each pattern foreach ($p in $pattern) { #define parameters to splat to Get-ChildItem #save errors to a variable to display later #suppressing error messages for now which are typically for access denied to system folders $dirParams = @{ Path = $path Recurse = $Recurse Force = $True ErrorAction = "SilentlyContinue" ErrorVariable = "ev" } if (-Not $regex) { $dirParams.add("Filter", $p) } Write-Verbose "...for $p" #not thrilled with this structure but it works if ($Regex) { Write-Verbose "...as regex" $results += (Get-ChildItem @dirParams).where( {$_.name -match $p}) } elseif ($Regex -AND $first) { Write-Verbose "...as regex" $results += (Get-ChildItem @dirParams).where( {$_.name -match $p}) | Select-Object -First 1 } elseif ($First) { $results += Get-ChildItem @dirParams | Select-Object -first 1 } else { $results += Get-ChildItem @dirParams } } #foreach p Write-Verbose "Evaluating results" #process errors foreach ($item in $ev) { if ($item.exception.getType().name -eq "UnauthorizedAccessException") { Write-Warning $item.exception.message } else { Write-Error "$($item.exception.message) [$($item.Exception.getType().name)]" } } } #if test path Else { Write-Warning "Failed to verify $Path" } } #foreach $count = ($results | Measure-Object).count write-verbose "Found $count matches" If (($count -gt 0) -And $Quiet) { #if Quiet and results found write $True $True } elseif (($count -eq 0) -And $Quiet) { $False } elseif (($count -gt 0) -AND $Full) { #if results found and write file results $results } Else { #else just write full name ($results).FullName } Write-Verbose "Ending $($MyInvocation.MyCommand)" } #end function |