How do I pass multiple variables to a powershell function?

Viewed 107

Just laying this out there. Here is my code for downloading an application and installing it.

# File Download and Install Function
function FDL($url){
# set to the default download directory; obviously can be wherever one wants
$DL = set-location $env:USERPROFILE\downloads\
# using this to capture just the filename
$FN = $url -split("/")
$FD = $FN[$FN.Length-1]
# Download File
Start-BitsTransfer -source $url -destination $DL\$FD
# Install File
Start-Process -NoNewWindow $DL\$FD -ArgumentList $args
}

PS:> FDL "https://www.kymoto.org/downloads/ISStudio_Latest.exe"

This function works perfectly every time assuming that the URL is correct!

Then I thought, what if I were to have the functionality to place the correct arguments for the installer type. So I came up with this:

# File Download and Install Function
function FDL($url,$p){
    # set to the default download directory; obviously can be whereever one wants
    $DL = set-location $env:USERPROFILE\downloads\
    # using this to capture just the filename
    $FN = $url -split("/")
    $FD = $FN[$FN.Length-1]
    
    switch ($p){
        1 {" /passive /qb /norestart";break}
        2 {" /sp- /silent /norestart /SUPPRESSMSGBOXES /CURRENTUSERS /NORESTART /NOCANCEL /FORCECLOSEAPPLICATION /RESTARTAPPLICATIONS";break}
        3 {" /SILENT";break}
        4 {" /quiet";break}
        5 {" /S";break}
        6 {" /Q";break}
    }
    
    Start-BitsTransfer -source $url -destination $DL\$FD
    Start-Process -NoNewWindow $DL\$FD -ArgumentList $p
}

# 2 because this is an InnoSetup   installer type
PS:> FDL 'https://www.kymoto.org/downloads/ISStudio_Latest.exe', 2 

FAIL

Start-BitsTransfer : The number of items specified in the Source parameter do not match the number of items specified in the Destination parameter. Verify that the same 
number of items is specified in the Source and Destination parameters.
At [dir]\FileDownloader Function.ps1:17 char:1
+ Start-BitsTransfer -source $url -destination $DL\$FD
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Start-BitsTransfer], ArgumentException
    + FullyQualifiedErrorId : StartBitsTransferArgumentException,Microsoft.BackgroundIntelligentTransfer.Management.NewBitsTransferCommand
Start-BitsTransfer : 
At [dir]\FileDownloader Function.ps1:17 char:1
+ Start-BitsTransfer -source $url -destination $DL\$FD
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Start-BitsTransfer], Exception
    + FullyQualifiedErrorId : System.Exception,Microsoft.BackgroundIntelligentTransfer.Management.NewBitsTransferCommand
Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty. Provide an argument that is not null or empty, and then try the 
command again.
At [dir]\FileDownloader Function.ps1:20 char:50
+ Start-Process -NoNewWindow $DL\$FD -ArgumentList ($p)
+                                                  ~~~~
    + CategoryInfo          : InvalidData: (:) [Start-Process], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartProcessCommand

No matter how I adjust this code it comes out with the same error. Any suggestions or assistance here will be greatly appreciated!

1 Answers

Might not be exactly what you're looking for but it should give you a hint as to approach the code for a function in PowerShell.

A few pointers, parameters in PowerShell are either Positional or Named, about_Parameters explains both concepts. Most importantly, each argument is separated by a space and not by a comma.

You can parse an URL using the Uri Class, so, for getting the file name from your address, is as simple as:

# Last segment from this Uri (index -1 from the segment array)
([uri] 'https://www.kymoto.org/downloads/ISStudio_Latest.exe').Segments[-1]

-ArgumentList from Start-Process expects string[], you can pass an array of arguments instead of a single string as shown in Example 7.

You're never capturing the output from your switch ($p), which explains the error:

Start-Process : Cannot validate argument on parameter 'ArgumentList'. The argument is null or empty.

A hash table can be used instead of a switch.

Lastly, I have added a -PassThru switch, now if you call the function with the switch activated (DownloadFile -PassThru -Uri ...), the function will output the Process instance representing the started process.

function DownloadFile {
    [cmdletbinding()]
    param(
        [parameter(Mandatory)]
        [uri] $Uri,

        [parameter()]
        [ValidateSet(1,2,3,4,5,6)]
        [int] $Arguments,

        [parameter()]
        [string] $Destination = "$env:USERPROFILE\Downloads",

        [parameter()]
        [switch] $PassThru
    )

    $arg = @{
        1 = '/passive', '/qb', '/norestart'
        2 = @(
            '/sp-', '/silent', '/norestart', '/SUPPRESSMSGBOXES'
            '/CURRENTUSERS', '/NORESTART', '/NOCANCEL'
            '/FORCECLOSEAPPLICATION', '/RESTARTAPPLICATIONS'
        )
        3 = '/SILENT'
        4 = '/quiet'
        5 = '/S'
        6 = '/Q'
    }

    $destFile = Join-Path $Destination -ChildPath $Uri.Segments[-1]
    Start-BitsTransfer -Source $Uri -Destination $destFile
    $param = @{
        FilePath     = $destFile
        ArgumentList = $arg[$Arguments]
        NoNewWindow  = $true
        PassThru     = $PassThru.IsPresent
    }
    Start-Process @param
}

DownloadFile -Uri 'https://www.kymoto.org/downloads/ISStudio_Latest.exe' -Arguments 2
Related