Question

I have a variable, $Tasks, in a PowerShell script that contains an array of strings, and I need to pass the variable along to other PowerShell scripts and do stuff with it, like loop through the array and do things with each item (string).

However, at some point along the way my variable gets converted from an array to a string (usually when it hits script2.ps1), and I'm not able to loop through it. What do I need to do to keep the variable as an array throughout the entire process?

Here is the workflow of the variable:

Script1.ps1:

$Tasks = @(
"Task1 - Name1",
"Task2 - Name2",
"Task3 - Name3"
)

powershell "& {. $pwd\Script2.ps1 -BuildNum $BuildNum; Run-Validation -Tasks $Tasks}"

Script2.ps1:

param(
    $Tasks=$()
)

Function Run-Validation
{
    param($Tasks)

    If ($Tasks)
    {
        Test-Tasks $Tasks
    }
}

Script3.ps1:

Function Test-Tasks ($Tasks)
{
    ForEach ($Task in $Tasks)
    {
        do_stuff
    }
}
Was it helpful?

Solution

The reason this is happening is that you're expanding an array, $Tasks, inside a double-quoted string. Before your command line is passed to PowerShell.exe, it is expanded to:

Arg 0 is <& {. C:\Script2.ps1 -BuildNum ; Run-Validation -Tasks Task1 - Name1 Task2 - Name2 Task3 - Name3}>

So the Run-Validation -Tasks parameter only sees "Task1". If you were to look at $args inside of the Run-Validation function you would see the rest of the arguments.

BTW, why invoke another Powershell.exe session? Why not just invoke like so:

. $PSScriptRoot\Script2.ps1 -BuildNum $BuildNum
Run-Validation -Tasks $Tasks

Note that the above only works if you eliminate the script level $Tasks parameter in Script2.ps1. If not, when you dot source Script2.ps1 to gain access to the Run-Validation function, the $Tasks in Script2.ps1 effectively overwrites the value set in Script1.ps1.

If you really want to invoke this in a separate PowerShell session you can do this:

$OFS="','"
powershell "& {. $pwd\Script2.ps1 -BuildNum $BuildNum; Run-Validation -Tasks '$Tasks'}"

OTHER TIPS

I find it helpful to structure my PowerShell scripts as functions. Instead of passing the $Tasks all the way through multiple scripts, I recommend pulling in the $Tasks when you are ready to use them.

This would be the equivalent of your Script1.ps1.

taskList.ps1

Function Get-Tasks()
{
    $Tasks = @(
        "Task1 - Name1",
        "Task2 - Name2",
        "Task3 - Name3"
    )
    return $Tasks
}

This would be the equivalent of your Script2.ps1 and Script3.ps1 combined:

# Dot source the scripts we will need
.$PSScriptRoot\taskList.ps1

# Get the list of tasks
$taskList = Get-Tasks

if ($taskList -ne $null)
{
    foreach ($task in $taskList)
    {
        # Do stuff here
        "Processing task: " + $task
    }
}

Note: $PSScriptRoot will load from the current working directory. If this file is located elsewhere, use the full path.

However, if you would still like to keep your current structure, you can simply update Script.1 to the following

# Dot source the scripts we will need
.$PSScriptRoot\Script3.ps1

$Tasks = @(
    "Task1 - Name1",
    "Task2 - Name2",
    "Task3 - Name3"
)

Run-Validation -Tasks $Tasks

I am using such a definition of parameters inside the script:

[cmdletbinding()]
Param(
    [Parameter(Position=0, ValueFromPipeline, ValueFromPipelineByPropertyName)]
    [ValidateNotNullorEmpty()]
    [Alias("cn", "name")]
    [String[]]$var
)

In this example you can define: [String]$varas a single string variable, or [String[]]$var as a array of string variables.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top