Search code examples
.netpowershellerror-handlingienumerableenumerable

How to get the result code when using .NET in powershell script?


I'm trying to get the result code from a running a file search in a powershell function. I'm using .NET for efficiency, and I don't want to compromise that. Now I am trying to obtain some kind of result code from the function (or command) but I don't how to do this.

I can save the command result in a variable, but if there are 10,000+ items, I'm not sure that is a great idea. Here's a POC using EnumerateFiles:

$RES=[IO.Directory]::EnumerateFiles($PWD, "*xxxx.dll", [IO.EnumerationOptions] @{AttributesToSkip='Device,Temporary,SparseFile,ReparsePoint,Compressed,Offline,Encrypted'; RecurseSubdirectories=$true; IgnoreInaccessible=$true})

$RES.Length
# Doesn't output anything! (Why is it not zero?)

$RES=[IO.Directory]::EnumerateFiles($PWD, "*.dll", [IO.EnumerationOptions] @{AttributesToSkip='Device,Temporary,SparseFile,ReparsePoint,Compressed,Offline,Encrypted'; RecurseSubdirectories=$true; IgnoreInaccessible=$true})

$RES.Length
# outputs the length of each path... NOT what I want.

Likewise using $? always returns True.

I was also thinking if it would be possible to use output redirection to measure the output somehow. Not sure how this would be done though.

How can I check the result success from a call like this?
(More specifically I would like to check if there are no files found.)


Solution

  • If you want to know if there are any items matching your filter you could use Enumerable.Any:

    function hasany {
        [CmdletBinding()]
        param([string] $Path = $PWD, [string] $Filter = '*')
    
        $Path = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
        $options = [IO.EnumerationOptions]@{
            AttributesToSkip      = 24384 # Same as Device, Temporary, ...
            RecurseSubdirectories = $true
            IgnoreInaccessible    = $true
        }
    
        [System.Linq.Enumerable]::Any(
            [System.IO.Directory]::EnumerateFiles($Path, $Filter, $options))
    }
    
    hasany -Filter *.dll
    

    Similarly, if you want to know how many, you could use Enumerable.Count. Both methods are very efficient, specially in .NET 9.

    function howmany {
        [CmdletBinding()]
        param([string] $Path = $PWD, [string] $Filter = '*')
    
        $Path = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
        $options = [IO.EnumerationOptions]@{
            AttributesToSkip      = 24384 # Same as Device, Temporary, ...
            RecurseSubdirectories = $true
            IgnoreInaccessible    = $true
        }
    
        [System.Linq.Enumerable]::Count(
            [System.IO.Directory]::EnumerateFiles($Path, $Filter, $options))
    }
    
    howmany -Filter *.dll
    

    Regarding the question in comments, how could you enable hasany *.dll and hasany ./dir *.txt, if you're using $args instead of named parameters you could do something like this, however there is no validation on how many arguments you're passing, it will only use $args[0] and $args[1] in this case.

    function hasany {
        $options = [IO.EnumerationOptions]@{
            AttributesToSkip      = 24384 # Same as Device, Temporary, ...
            RecurseSubdirectories = $true
            IgnoreInaccessible    = $true
        }
    
        $params = $args.Count -eq 2 ?
            (Convert-Path $args[0]), $args[1], $options :
            $pwd.Path, $args[0], $options
    
        [System.Linq.Enumerable]::Any(
            [System.IO.Directory]::EnumerateFiles.Invoke($params))
    }