Scheduled tasks, PowerShell’s -file parameter, and array values

I wrote a script that accepts a comma-separated list of values, and the script worked just fine from the command-line. However, when I tried to configure a scheduled task to run the script, it always failed.

Why? Well, I started a cmd.exe session and then launched the script in the same way that the scheduled task did, using PowerShell’s -file parameter. And when I did that, the error message that I emit from the script showed me that the list was being parsed as a single string argument.

To confirm and experiment, I wrote a short little test script:

<# Cast-WizardSpell.ps1 
.SYNOPSIS 
Simple script to test parameter parsing when using -file invocation e.g.: 
powershell.exe -file .\Cast-WizardSpell -Spell 'Light','Magic Missile' 
#>
[cmdletbinding()]
param(
    [Parameter(Mandatory=$True,ValueFromPipeline=$True)]
    [string[]]
    $Spells
)
process {

    foreach ($spell in $spells ) {
        "Casting $spell"
    }
}

When run from within a PowerShell session, it works as expected:


PS C:\> .\Cast-WizardSpell.ps1 -SpellList 'Ray of Frost','Light','Detect Magic'
Casting Ray of Frost
Casting Light
Casting Detect Magic

When invoked using the PowerShell -file parameter, the comma-separated list is parsed as a single parameter (note: cmd.exe doesn’t like single quotes):


C:\>powershell -file .\Cast-WizardSpell.ps1 -SpellList "Ray of Frost","Light","Detect Magic"
Casting Ray of Frost,Light,Detect Magic

# Trying explicit array syntax, but no luck

C:\>powershell -file .\Cast-WizardSpell.ps1 -SpellList @("Ray of Frost","Light","Detect Magic")
Casting @(Ray of Frost,Light,Detect Magic)

What does work is to use the old-style -command syntax:


C:\>powershell -command "& .\Cast-WizardSpell.ps1 -SpellList 'Ray of Frost','Light','Detect Magic'"
Casting Ray of Frost
Casting Light
Casting Detect Magic

Alternatively, one can adjust the parameter syntax, adding the ValueFromRemainingArguments attribute. However, for this to work, you can’t specifiy the parameter name.


C:\>powershell -file .\Cast-WizardSpell.ps1  "Ray of Frost" "Light" "Detect Magic"
Casting Ray of Frost
Casting Light
Casting Detect Magic

C:\local\scripts>powershell -file .\Cast-WizardSpell.ps1 -SpellList "Ray of Frost" "Light" "Detect Magic"
C:\local\scripts\Cast-WizardSpell.ps1 : A positional parameter cannot be found that accepts argument 'Light'.
+ CategoryInfo          : InvalidArgument: (:) [Cast-WizardSpell.ps1], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Cast-WizardSpell.ps1

I’m not thrilled with either of these options, because some person like me may come along and, in an effort to be helpful, may twiddle the command line, thinking we’re normalizing or updating the syntax, when we’re really breaking things. However, I think using the -Command invocation is the least surprising, most consistent implementation. I’ll just make notes in the script help and in the description of the scheduled task about the reason I’ve used that method.

 

 

 

Leave a Reply