Powershell: Different behaviour of "$_.Split("",[StringSplitOptions]"RemoveEmptyEntries")" in powershell 5.1 vs 6.0 core

Created on 10 Mar 2020  路  7Comments  路  Source: PowerShell/PowerShell

Steps to reproduce

PSVersion : 6.2.4
PSEdition : Core
GitCommitId : 6.2.4
OS : Microsoft Windows 10.0.18363
Platform : Win32NT
PSCompatibleVersions : {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion : 2.3
SerializationVersion : 1.1.0.1
WSManStackVersion : 3.0

helm search stable -l -r "?.kafka" | ForEach-Object { $_.Split("",[StringSplitOptions]"RemoveEmptyEntries")[1] }

Output is empty

Expected behavior

PSVersion : 5.1.18362.628
PSEdition : Desktop
PSCompatibleVersions : {1.0, 2.0, 3.0, 4.0...}
BuildVersion : 10.0.18362.628
CLRVersion : 4.0.30319.42000
WSManStackVersion : 3.0
PSRemotingProtocolVersion : 2.3
SerializationVersion : 1.1.0.1

helm search stable -l -r "?.kafka" | ForEach-Object { $_.Split("",[StringSplitOptions]"RemoveEmptyEntries")[1] }

CHART
2.3.0
2.2.1
2.2.0
2.1.6
2.1.5
2.1.4
2.1.3
1.1.3
1.1.2
1.1.1
1.1.0
1.0.0

Actual behavior

When doing the split by "" in PS core it no longer not takes into consideration a non-printable characters as it does in PS 5.1
I checked the hash of the non-printable characters and they are the same in both cases (PS 5.1 and PS core)

Issue-Question

Most helpful comment

Indeed, this has come up several times before, and the bottom line is:

  • New overloads in the CLR can break PowerShell scripts at any time, because PowerShell is late-bound, and new overloads may be selected due to being a better type fit than a previous one.

  • For long-term stability, stick with PowerShell-native features or be sure to use casts to precisely match the types of the invoked .NET method's parameters.

    • $_.Split([char[]] "", "RemoveEmptyEntries") would give you the old behavior, as @rjmholt notes.

See https://github.com/PowerShell/PowerShell/issues/11720#issuecomment-579866445

In the case at hand, a PowerShell-native solution is also much more concise, using the unary form of the -split operator, which treats runs of whitespace as a single separator (and also ignores leading and trailing whitespace):

'stable/kafka-manager   2.3.0           1.3.3.22    A tool for managing Apache Kafka.',
'stable/kafka-manager   2.2.1           1.3.3.22    A tool for managing Apache Kafka'  | 
  ForEach-Object { (-split $_)[1] }

The above yields 2.3.0 and 2.2.1.

All 7 comments

Could you give an example of the outputs of helm search stable -l -r "?.kafka" -- we need to be able to reproduce without helm installed.

It looks like the behavioural change you're experiencing may be occurring in the String.Split() method, which would indicate a .NET rather than a PowerShell change, meaning it's likely something we're unable to fix.

However, we may be able to come up with a technique for mitigating the change.

Also, if you can, try this in PowerShell 7.0 to confirm which behaviour you're seeing.

@rjmholt

The issue is also reproducible with PS Version 7.0
The output might be a little bit tricky to send in here because of the format.
Therefore I attached the output in in Plain Text and in Hex in order to preserve the raw format of the output.
Hex.txt
PlainText.txt

Please let me know if this helps you.

Yeah that PlainText.txt file reproduces it nicely.

This might actually be a break in the way we find overloads in PowerShell, or it might be that .NET Core introduced a new overload that we're now resolving.

You can get the old behaviour with this:

gc 'C:\Users\Robert Holt\Desktop\PlainText.txt' | ForEach-Object { $_.Split([char[]]@(), [StringSplitOptions]"RemoveEmptyEntries")[1] }

/cc @daxian-dbw

or it might be that .NET Core introduced a new overload that we're now resolving

Actually I think it may well be this: https://github.com/dotnet/runtime/issues/14483.

Indeed, this has come up several times before, and the bottom line is:

  • New overloads in the CLR can break PowerShell scripts at any time, because PowerShell is late-bound, and new overloads may be selected due to being a better type fit than a previous one.

  • For long-term stability, stick with PowerShell-native features or be sure to use casts to precisely match the types of the invoked .NET method's parameters.

    • $_.Split([char[]] "", "RemoveEmptyEntries") would give you the old behavior, as @rjmholt notes.

See https://github.com/PowerShell/PowerShell/issues/11720#issuecomment-579866445

In the case at hand, a PowerShell-native solution is also much more concise, using the unary form of the -split operator, which treats runs of whitespace as a single separator (and also ignores leading and trailing whitespace):

'stable/kafka-manager   2.3.0           1.3.3.22    A tool for managing Apache Kafka.',
'stable/kafka-manager   2.2.1           1.3.3.22    A tool for managing Apache Kafka'  | 
  ForEach-Object { (-split $_)[1] }

The above yields 2.3.0 and 2.2.1.

Thanks for feedback guys I will close the issue since the new behaviour its coming from .Net Core and not from PS.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JohnLBevan picture JohnLBevan  路  3Comments

aragula12 picture aragula12  路  3Comments

alx9r picture alx9r  路  3Comments

Michal-Ziemba picture Michal-Ziemba  路  3Comments

garegin16 picture garegin16  路  3Comments