1

I'm trying to make a Powershell function that takes in a piped string (from ExpandProperty).

for some reason I keep getting a null error (attached below), but if I run the code without the last piped command it works fine. I tried wrapping the Get-ChildItem pipeline in an array (@) too, but that didn't help.


error code

Get-FolderSize : Cannot bind argument to parameter 'folderpath' because it is null.
At line:31 char:106
+ ... ainer } |  Select-Object -ExpandProperty FullName | Get-FolderSize $_
+                                                                        ~~
    + CategoryInfo          : InvalidData: (:) [Get-FolderSize], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-FolderSize

code


$dirpath = "C:\SomeFolder"

Function Get-FolderSize {
    # allows cmd options 
    [cmdletbinding()]
    Param ( 
        [Parameter(Mandatory=$true, Position=0)]
        $folderpath = $(Throw "no folder name specified")
    )
    if ($foldepath -eq $null){
        return 0 
    }
    $folderpath = Convert-Path {$folderpath}

    $size = 0

    # calculate folder size and recurse as needed
    Foreach ($file in $(ls $folderpath -recurse)){
     If (-not ($file.psiscontainer)) {
        $size += $file.length
        }
    }

    # return the value and go back to caller
    return $size
}
$Files = @(gci $dirpath -Recurse | ?{ $_.PSIsContainer } |  Select-Object -ExpandProperty FullName) 
if ($Files.length -eq 0) {
  write-host "   no files to delete." 
} else {
  $Files
}

2
  • 3
    In order for your function to receive objects from the pipeline at least one of your parameters must be set to allow values from pipeline. You will also need a Process {} block. Give this a read - devblogs.microsoft.com/scripting/… Commented Feb 24, 2021 at 0:19
  • As it wasn't specifically mentioned in the existing answer, you don't need to use PSIsContainer, i.e. ls $folderpath -file -recurse and gci $dirpath -File -Recurse, unless your script needs to run on a PowerShell version before v3.0. Commented Feb 24, 2021 at 2:03

1 Answer 1

1

Since you are learning, there are a few things I can point out. Indeed if you use advanced parameters and [cmdletbinding()] as you have here you need Begin{}, Process{} & End{} blocks. The process block will be run once for every object passed down the pipeline.

There are also a number of things you can do a lot easier in PowerShell:

Function Get-FolderSize {

    [cmdletbinding()]
    Param ( 
        [Parameter( Mandatory = $true, Position = 0, ValueFromPipeline )]
        [String[]]$FolderPath
    ) # End Param Block
    
    Begin{}

    Process
    {
        [UInt64](Get-ChildItem $FolderPath -Recurse -File |
        Measure-Object -Property Length -Sum).Sum
    } # End Process Block
    
    End{}
} # End Function Get-FolderSize
  • You don't really need to throw an error for the FolderPath parameter. You've already made it mandatory. If it isn't specified, the function will prompt for it.
  • Add ValueFromPipeline parameter attribute, Begin{}, Process{} & End{} blocks to enable pipeline input.
  • I'm not sure what Convert-Path was doing there, doesn't look necessary.
  • Use the -File switch parameter with Get-ChildItem to recursively list all the files without having to perform logic on .PSIsContainer.
  • Use Measure-Object to sum the file sizes for you emit its sum property. Notice you don't need to assign that to a variable, or use the return statement. Anything emitted from the function will be returned to the caller.

Note: I also returned the value casted to a [UInt64] numeric type. This helps in flipping null returns to zero without disallowing very large numbers.

I also casted the -FolderPath parameter as a string array ([string[]]). I'm not sure if your intent is really to allow multiple objects down the pipe, but if you do it will return a sum for each path you give it. At any rate this is how I would start a function like this.

I'm not sure what you were doing with the rest of the code, I don't see where you were calling the function. However, I get the feeling you wanted to list the folders with their sizes. With the above function in hand, that might look something like:

Get-ChildItem c:\temp -Directory | 
Select-Object *,@{Name = 'FolderSize'; Expression = { Get-FolderSize $_.FullName }} | 
Format-Table FullName,FolderSize -Wrap

There's obviously a lot more to writing advanced functions. There is no shortage of reading material, just google it...

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.