user5544
user5544

Reputation: 171

Converting System.Collections.ArrayList to a string in Powershell

I have the following test script in powershell, the issue I am facing is that I want to store the details of the error in a custom object or a variable as a string, right now the value of the error variable is of type System.Collections.ArrayList.

invoke-sqlcmd -ServerInstance "$sql" -inputfile $file  -Database test -OutputSqlErrors $true -ConnectionTimeout 10 -ErrorAction Continue -Errorvariable err | Out-Null

Now, here is what is strange, if I run

$err | Get-Type

its of type System.Collections.ArrayList

if i run write-host $err, it prints out the error correctly, but assigning the value of $err to a custom object, then I lose the value but get 'System.Collections.ArrayList' instead.

$error_values += New-Object -TypeName psobject -Property @{ErrorMessage =$err}

Now running $error_values | select ErrorMessage returns System.Collections.ArrayList

I just need it to be a simple string, not sure what is incorrect here.

Upvotes: 2

Views: 7288

Answers (2)

mklement0
mklement0

Reputation: 438188

  • Generally, don't use Write-Host to output data, as that won't work; use it for display-only output only; to output data, use Write-Output, or, better yet, use PowerShell's implicit output feature.

    • In your case, $error_values | select ErrorMessage by itself would not only output the information of interest as data, it would also have resulted in helpful output formatting.

    • See this answer for more information.

  • Specifically, even when for-display-only output is desired, don't use Write-Host to print representations of complex objects (loosely speaking, objects with properties);
    use Out-Host instead.

    • $error_values | select ErrorMessage | Out-Host
    • As an aside: the select (Select-Object) call is virtually a no-op; I presume what you actually meant was $error_values | select -ExpandProperty ErrorMessage - see this answer for more information.
    • Write-Host does not perform rich output formatting, whereas PowerShell's default display formatting and Out-Host do. Since Write-Host uses simple .ToString() stringification, complex objects usually result in unhelpful representations - see this answer for more information.

A complete example:

# Create a sample ArrayList, as returned by 
$err = [System.Collections.ArrayList] ('one', 'two')

# Construct a custom object ([pscustomobject]) with an .ErrorMessage
# property that contains the array list
$obj = [pscustomobject] @{ ErrorMessage = $err }

# !! Write-Host results in UNHELPFUL display:
# !! Note how instead of listing the *elements* of the ArrayList
# !! the *type name* ("System.Collections.ArrayList") is printed instead.
PS> Write-Host $obj
@{ErrorMessage=System.Collections.ArrayList}

# OK: Out-Host produces richly formatted *display-only* output.
PS> $obj | Out-Host

ErrorMessage
------------
{one, two}


# OK as well: *implicit* output, *as data* - same representation.
PS> $obj

ErrorMessage
------------
{one, two}

Note: For direct to-display output of a collection whose elements are not complex objects, Write-Host can be helpful if single-line output is desired, because the elements are then simply space concatenated:

$err = [System.Collections.ArrayList] ('one', 'two')

# OK with collection of non-complex objects to get a 
# *single-line* representation.
# Out-Host and implicit output print each element on its own line.
PS> Write-Host $err
one two

You can even use -Separator to use a separator other than a space:

PS> Write-Host -Separator /  'one', 'two'
one/two

Of course, you can also use an expression to create such a string, using the -join, the string joining operator operator, which would allow you to use the output as data:

PS> 'one', 'two' -join '/'
one/two

Optional reading: Why a target variable passed to -ErrorVariable receives a System.Collections.ArrayList instance:

The common -ErrorVariable parameter - and indeed all common -*Variable parameters that collect a stream's output in a variable - always return a System.Collections.ArrayList instance with the collected output, which is surprising in two respects:

  • PowerShell generally uses [object[]] arrays as its default collection data type.

  • Additionally, in the case of -OutVariable, the behavior is inconsistent with direct output from the pipeline, where a single object output from a command is output as such - rather than being wrapped in a collection, such as ArrayList in this case; that is, even though you would expect the following two commands to be equivalent, they're not:

    $out = Get-Date  
    # -> $out is a [datetime] instance.
    
    # !! -OutVariable *always* creates ArrayList.
    $null = Get-Date -OutVariable out 
    # -> $out is a *single-element ArrayList* containing a [datetime] instance.
    

For a discussion of these inconsistencies, see GitHub issue #3154.

Upvotes: 1

$arrayList = [System.Collections.ArrayList]::new()

[void]$arrayList.Add("one")
[void]$arrayList.Add("two")
[void]$arrayList.Add("three")

$msg = $arrayList -join ", "
Write-Host $msg

Upvotes: 3

Related Questions