2

Here's something I don't understand.

When I define a variable:

$v = [byte]2, [byte]3

and check its type:

$v.getType().name

I get

Object[]

I then format $v:

'{0} {1}' -f $v

which prints

2 3

Now, if I get a file's first two bytes:

$f = (get-content 'xyz.txt' -encoding byte -readCount 2 -totalCount 2)

and check its type:

$f.getType().name

I get the same type as before: Object[].

However, unlike with $v, I cannot format $f:

'{0} {1}' -f $f

I get the error message Error formatting a string: Index (zero based) must be greater than or equal to zero and less than the size of the, although the length of the array is 2:

$f.length

returns

2

I don't understand why this is and would appreciate an explanation.

1 Answer 1

4
  • The behavior should be considered a bug in the -f operator; it is present as of v7.1 and reported in GitHub issue #14355; it does not affect other operators with array operands, such as -split or -in.

  • The workaround is to cast $f to [array] or, if creating a copy of the array is acceptable, @($f):

'abc' > xyz.txt

$f = get-content 'xyz.txt' -encoding byte -readCount 2 -totalCount 2

'{0} {1}' -f ([array] $f)

Note: Using @(), the array-subexpression operator - ... - @($f) - as Mathias R. Jessen notes - is the even simpler option, but do note that using @() involves cloning (creating a shallow copy of) the array, whereas the [array] cast in this case does not.

The alternative is to apply the [array] cast as a type constraint (by placing it to the left of the $f = ... assignment):

'abc' > xyz.txt

[array] $f = (get-content 'xyz.txt' -encoding byte -readCount 2 -totalCount 2)

'{0} {1}' -f $f

Note:

  • In PowerShell [Core] v6+, you must use -AsByteStream in lieu of -Encoding Byte.

  • The problem can also be avoided if -ReadCount 2 is omitted, but note that that decreases the performance of the command, because the bytes are then emitted one by one; that is, with -ReadCount 2 -TotalCount 2 a single object is emitted that is a 2-byte array as a whole, whereas just -TotalCount 2 emits the individual bytes, one by one to the pipeline, in which case it is then the PowerShell engine itself that collects these bytes in an [object[]] array for the assignment.

  • Note that applying @() directly to the command - @(get-content ...) - would not work in this case, because @(), due to parameter combination -ReadCount 2 -TotalCount 2, receives a single output object that happens to be an array as a whole and therefore wraps that single object in another array. This results in a single-element array whose element is the original 2-element array of bytes; for more information about how @(...) works, see this answer.


Background information:

The problem is an invisible [psobject] wrapper around each array returned by Get-Content -ReadCount (just one in this case), which unexpectedly causes the $f array passed to -f not to be recognized as such.

Note that PowerShell's other array-based operators, such as -in and -replace, are not affected.

The wrapper can be bypassed in two ways:

  • $f.psobject.BaseObject

  • casting to [array], as shown at the top.

Note:

  • Generally, output objects produced by cmdlets - as opposed to output produced by PowerShell code - have generally invisible [psobject] wrappers; mostly, they are benign, because PowerShell usually just cares about the .NET object being wrapped, not about the wrapper, but on occasion problems arise, such as in this case - see GitHub issue #5579 for a discussion of the problem and other contexts in which it manifests.

  • In order to test if a given object has a [psobject] wrapper, use -is [psobject]; e.g.:

$var = 1
$var -is [psobject] # -> $false

$var = Write-Output 1
$var -is [psobject] # -> $true, due to use of a cmdlet.

# You can also test command output directly.
(Write-Output 1) -is [psobject]  # -> $true
Sign up to request clarification or add additional context in comments.

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.