3

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.

2 Answers 2

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

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

$msg = $arrayList -join ", "
Write-Host $msg
Sign up to request clarification or add additional context in comments.

Comments

2
  • 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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.