Getting a function to work with multiple values

Welcome Forums General PowerShell Q&A Getting a function to work with multiple values

This topic contains 7 replies, has 3 voices, and was last updated by

js
 
Participant
5 days ago.

  • Author
    Posts
  • #114033

    Participant
    Points: 0
    Rank: Member

    Hello!

    I'm pretty new to writing functions and I'm trying to create one that displays volume/disk sizes in a certain way.

    The code I have works as I would like, but only if I specify one drive at a time, i.e:

    If I type "get-disksize -Driveletter C" without the quotes, it returns this:

    Driveletter SizeGB FreeSpaceGB FreeSpace%

    ———– —— ———– ———-

    C           233    105         45

     

    I can also send those object down the pipeline so it works with select, sort, etc. That's all fine, so I just need to get it working with multiple drives. If I type more than one drive in such as C,F then it shows this error:

     

    Attempted to divide by zero.

    At (scriptpath)27 char:9

    +         $Prop=[ordered]@{

    +         ~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [], RuntimeException

    + FullyQualifiedErrorId : RuntimeException

     

    I would also like to be able to just type Get-disksize and it will display the info for all drive letters if possible!

    Any help greatly appreciated!

     

    Thanks

     

    function Get-Disksize
    {
        [CmdletBinding(DefaultParameterSetName="None")]
        [Alias()]
        [OutputType([int])]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$false,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            $Driveletter
        )
    
        Begin
        {
        }
        Process
        {
            foreach($Drive in $Driveletter){
            $disk=Get-Volume -DriveLetter $Driveletter
            }
            $FSGB=$disk.sizeremaining -as [double]
            $Size=$disk.size -as [double]
    
            $Prop=[ordered]@{
                'Driveletter'=($Driveletter.ToUpper())
                'SizeGB'="{0:0}" -f ($Size/1GB)
                'FreeSpaceGB'="{0:0}" -f ($FSGB/1GB)
                'FreeSpace%'="{0:0}" -f (($FSGB/$Size)*100)
            }
            $Obj=New-Object -TypeName PSObject -Property $Prop
            Write-Output $Obj
            }
    
        End
        {
        }
    }
    
  • #114045

    Participant
    Points: 0
    Rank: Member

    Few changes are needed here:
    1. Line should say $disk=Get-Volume -DriveLetter $Drive # not $DriveLetter
    2. The foreach loop should enclose the entire code block all the way to write-output $obj
    3. 'Driveletter'=$DriveLetter.ToUpper() should also reference $Drive and not $DriveLetter
    4. You should define the $DriveLetter input data type as in: [String[]]$Driveletter instead of just $DriveLetter
    5. Finally, to setup default input, the $DriveLetter parameter should read: [String[]]$Driveletter = (Get-Volume | where {$_.DriveLetter}).DriveLetter
    I would also remove the unneeded CmdletBinding arguments.
    Additionally you want to consider scenarios of bad input like non-existing drive letters or wrong data type input

    Here's the updated code:

    function Get-Disksize
    {
        [CmdletBinding()]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$false,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            [String[]]$Driveletter = (Get-Volume | where {$_.DriveLetter}).DriveLetter
        )
    
        Begin
        {
        }
        Process
        {
            foreach($Drive in $Driveletter){
                $disk=Get-Volume -DriveLetter $Drive
                $FSGB=$disk.sizeremaining -as [double]
                $Size=$disk.size -as [double]
    
                $Prop=[ordered]@{
                    'Driveletter'=$Drive.ToUpper()
                    'SizeGB'="{0:0}" -f ($Size/1GB)
                    'FreeSpaceGB'="{0:0}" -f ($FSGB/1GB)
                    'FreeSpace%'="{0:0}" -f (($FSGB/$Size)*100)
                }
                $Obj=New-Object -TypeName PSObject -Property $Prop
                Write-Output $Obj
            }
        }
    
        End
        {
        }
    }
    
    • #114055

      Participant
      Points: 0
      Rank: Member

      Wow, thank you, that makes more sense and works like a charm!

      I had tried the [string[]] before, but I took it out just in case it was causing any issues.

      🙂

       

  • #114057

    Participant
    Points: 0
    Rank: Member

    So now that's working, I added another line to the hash table called Name, which gets it's info from the filesystemlabel property, but after adding this, the results come out as a list instead of a table as they were before. Is there a reason this happens? All the data is correct, it's just changed the default formatting for some reason!

     

    function Get-Disksize
    {
        [CmdletBinding()]
        Param
        (
            # Param1 help description
            [Parameter(Mandatory=$false,
                       ValueFromPipeline=$true,
                       ValueFromPipelineByPropertyName=$true,
                       Position=0)]
            [String[]]$Driveletter = (Get-Volume | where {$_.DriveLetter}).DriveLetter
        )
    
        Begin
        {
        }
        Process
        {
            foreach($Drive in $Driveletter){
                $disk=Get-Volume -DriveLetter $Drive
                $FSGB=$disk.sizeremaining -as [double]
                $Size=$disk.size -as [double]
                
                $Prop=[ordered]@{
                    'Driveletter'=$Drive.ToUpper()
                    'Name'=$disk.FileSystemLabel
                    'SizeGB'="{0:0}" -f ($Size/1GB)
                    'FreeSpaceGB'="{0:0}" -f ($FSGB/1GB)
                    'FreeSpace%'="{0:0}" -f (($FSGB/$Size)*100)
                }
                $Obj=New-Object -TypeName PSObject -Property $Prop
                Write-Output $Obj
            }
        }
    
        End
        {
        }
    }
    
  • #114066
    js

    Participant
    Points: 6
    Rank: Member

    That's just the default behavior with powershell. With 5 properties it implicitly runs format-list instead of format-table. You can either pipe your function to format-table (ft), or make a format.ps1xml file for your object, setting a default table view. https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_format.ps1xml?view=powershell-6

  • #114069

    Participant
    Points: 0
    Rank: Member

    OK thanks. I'll give that a go 🙂

  • #114082

    Participant
    Points: 0
    Rank: Member

    I have now created a format.ps1xml file and it displays everything as a table now. However, if I pipe the command and use select and only select certain properties, such as driveletter, it still shows the headings for all of the other properties, and they are blank.

    The view I set up was for the typename "System.Management.Automation.PSCustomObject". I used this because if I run get-disksize | gm then it shows that as the typename.

    I then ran get-disksize | select driveletter | gm and it gives me the typename as:

    Selected.System.Management.Automation.PSCustomObject

    It's basically the same as the other typename, but with selected at the front, which makes sense. If I just want it to display the columns I have selected, do I need to create another view for the selected type name or is there a way I can say only show the column if it contains data?

    I've included the XML code I'm using. It might need cleaning up a bit as I copied it from another module.

    I tried to post the XML code, but I don't think the website like it as it's XML. Is there a way to post XML code like I post the powershell code?

    Thanks

  • #114112
    js

    Participant
    Points: 6
    Rank: Member

    The format file will only work for whatever you name the object (I would give it a custom name with the pstypename property). Once you pipe it to select, it's a different object. Here's my format file from gist. It's from this thread:
    https://powershell.org/forums/topic/output-5-properties-as-a-table/

You must be logged in to reply to this topic.