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:
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