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
    #tags are used for categorizing the command
    #cmdTags = file
    Write-Verbose "Starting $($MyInvocation.MyCommand)"
    Write-Verbose "Running under PowerShell version $($PSVersionTable.PSVersion)"
    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 location $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


#EOF