在每一行结束后拆分文本

Split text after each end of line

我有一个脚本可以在 Powershell 5.x 上完美运行,但在 Powershell Core (7.2.1) 上不再运行

当我尝试拆分文本(从电子邮件复制并粘贴)时出现问题..

这一切都归结为这部分代码:

$test="blue
green
yellow
"

#$test.Split([Environment]::NewLine)

$x = $test.Split([Environment]::NewLine)

$x[0]
$x[1]

在 Powershell 5 中 $x[0]==blue$x[1]==green 的值 但在 Powershell Core 中,拆分没有任何作用,$x[1] 是“不存在的”。

在 Powershell 7 中,换行符的处理方式有所不同(至少我是这么认为的),但我找不到解决方法..

我尝试将代码更改为 $rows = $path.split([Environment]::NewLine)$path.Split([System.Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries) 但这并没有改变任何东西..

此外,当我使用“here-string”时

$test = @'
green

yellow
blue

white
'@
$x= $test -split "`r`n", 5, "multiline"

除了 $x[0] 之外的所有内容都是空的(即 $x[2]

我已经在看这里了:https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_split?view=powershell-7.2

这里:

这里:

到目前为止我还没有找到解决问题的方法。

感谢任何帮助。

编辑:我找到了关于该问题的提示,但还不明白它的含义:https://n-v-o.github.io/2021-06-10-String-Method-in-Powershell-7/

编辑 2: 感谢大家参与回答我的问题。 首先,我想我要写一个很长的解释,为什么我的问题与@SantiagoSquarzon 的重复答案不同。但是在阅读我的问题和另一个问题的答案时,我注意到我在做一些不同的事情..

显然我使用时有些不同

$splits = $test -split '`r?`n' # doesn't work in 5.1 and 7.2.1
$splits = $test -split '\r?\n' # works in 5.1 and 7.2.1 as suggested from Santiago and others

但是

$splits = $test.Split("\r?\n") # doesn't work in 5.1 and 7.2.1
$splits = $test.Split("`r?`n") # doesn't work in 5.1 and 7.2.1
$splits = $test.Split([char[]]"\r\n") # doesnt' work in 7.2.1
$splits = $test.Split([char[]]"`r`n") # works in 7.2.1

感谢这个网站,我找到了一个解决方案: https://n-v-o.github.io/2021-06-10-String-Method-in-Powershell-7/

In .NET 4, the string class only had methods that took characters as parameter types. PowerShell sees this and automagically converts it, to make life a little easier on you. Note there’s an implied ‘OR’ (|) here as it’s an array of characters.

Why is PowerShell 7 behaving differently? In .NET 5, the string class has some additional parameters that accept strings. PowerShell 7 does not take any automatic action.

为了解决我的问题,我不得不使用这个:

$test.Split("`r").Split("`n") #or
$test.Split([char[]]"`r`n")

原因:将文本粘贴到终端时,使用哪个终端很重要。默认的 powershell 5.1、ISE 终端和大多数其他 Windows 软件使用两个回车分隔新行 return \r 换行符 \n人物。我们可以通过转换为字节来检查:

# 5.1 Desktop
$test = "a
b
c"
[byte[]][char[]]$test -join ','

97,13,10,98,13,10,99
#a,\r,\n, b,\r,\n, c

Powershell Core 用一个换行\n字符

分隔新行
# 7.2 Core
$test = "a
b
c"
[byte[]][char[]]$test -join ','

97,10,98,10,99

在 Windows OS 上,无论哪个控制台,[Environment]::NewLine 都是 \r\n。 Linux 上是 \n.


解决方案:在 \r\n \n 上拆分多行字符串(但不仅限于 \r).这里最简单的方法是使用正则表达式,就像@Santiago-squarzon 建议的那样:

$splits = $test -split '\r?\n'
$splits[0]
a
$splits[1]
b

tl;dr:

  • 使用-split '\r?\n将多行文本拆分为多行不管Windows-format CRLF还是Unix -使用格式 LF 换行符(它甚至可以在单个字符串中处理这些格式的 mix)。

  • 如果您还想处理 CR-only 换行符(这不常见,但对您来说似乎就是这种情况),请使用 -split '\r?\n|\r'

  • 在 Windows 上,仅使用 CRLF 换行符,.Split([Environment]::NewLine) 仅在 PowerShell (Core) 7+ 中按预期工作,而不是在 Windows PowerShell 中(而且,意外地,在 Windows PowerShell 中 with CR-only 换行符,就像你的情况一样。)要仅按 CR 明确拆分,.Split("`r") 会恰好按预期在 both 版本中工作,因为按 只有一个字符。

# Works on both Unix and Windows, in both PowerShell editions.
# Input string contains a mix of CRLF and LF and CR newlines.
"one`r`ntwo`nthree`rfour" -split '\r?\n|\r' | ForEach-Object { "[$_]" }

输出:

[one]
[two]
[three]
[four]

这是最稳健的方法,因为您通常可以 依赖输入文本来使用 platform-native 换行格式, [Environment]::NewLine;详情见底部。

注:

  • 上面使用了PowerShell的-split operator, which operates on regexes (regular expressions),实现了上面的灵活匹配逻辑

  • 相比之下,System.String.Split() .NET 方法 仅拆分 literal strings,虽然速度更快,但会限制您查找 verbatim 分隔符。

  • 语法的含义是:

    • Regex 结构,例如 转义序列 \r (CR) 和 \n (LF)受 .NET regex 引擎 支持,因此仅受 -split(以及其他使用正则表达式的 PowerShell 上下文)支持;正则表达式元字符 ?(匹配前面的子表达式零次或一次)和 |(交替;匹配任一侧的子表达式)也是如此。
      strings 中(正则表达式必须在 PowerShell 中表示,最好在 '...' 中),这些序列和字符具有 no 特殊含义,既不是 PowerShell 本身,也不是 .Split() 方法,后者将它们全部 逐字 .

    • 相比之下,类似的转义序列"`r"(CR)和"`n"(LF)是PowerShell features,在 expandable strings, i.e. they work only inside "..." - not also inside verbatim strings'...' 中可用 - 并在目标运算符、方法或命令看到 之前 扩展为它们代表的字符结果字符串。

  • 更深入地讨论了 -split.Split() 并推荐常规使用 -split.


至于你试过的

  • 使用[Environment]::NewLine 如果您确定输入字符串使用平台原生 换行格式。值得注意的是,在 PowerShell 提示 交互式输入的多行字符串文字甚至在 Windows 上也使用 Unix 格式的 LF 换行符(唯一的例外是 obsolescent Windows -仅 ISE,它使用 CRLF)。

  • 脚本文件*.ps1)中的字符串文字使用与脚本保存在 - 这可能是也可能不是平台的格式。

  • 此外,正如您在自己的回答中提到的,在 .NET Core / .NET 5+ 的 System.String.Split() 方法中添加了 string 参数重载 -因此 PowerShell (Core) v6+ - 相对于 Windows PowerShell 隐含地导致了重大变化 ]:具体来说,.Split('ab')'a''b' 拆分 - 即由 组成字符串的任何单个字符 - 在 Windows PowerShell 中,而在 PowerShell 中它按 整个字符串 'ab' 拆分(核心)v6+.

    • 这种隐含的重大变化很少见,但确实会发生,而且它们不在 PowerShell 的控制范围内。

    • 出于这个原因,您应该始终更喜欢 PowerShell-native 功能以获得长期稳定性,在这种情况下,这意味着更喜欢-split operator.Split() .NET method.

      • 也就是说,出于 性能原因,有时 .NET 方法更可取;您可以使它们稳健地工作,但前提是仔细匹配感兴趣的方法重载的确切数据类型,这可能需要cast;见下文。
    • 有关详细信息,请参阅 ,包括对隐式重大更改的更详细说明。

您对 -split '\r?\n' 不适合您的反馈和 中的解决方案建议您的输入字符串 - 不寻常地 - 使用 仅 CR 换行符

您的答案的解决方案不会按预期使用Windows格式的 CRLF 格式文本,因为每个 CR 和 LF 都会单独发生拆分,这会在输出数组中产生额外的空元素(每个元素代表 CRLF 序列“之间”的空字符串)。

如果您确实想在 Windows 上按 [Environment]::NewLine 拆分 - 即按 CRLF - 并且您希望坚持使用 .Split() 方法,以使其在 Windows PowerShell 也,你需要调用需要 [string[]] 参数的重载,表明要使用每个字符串(即使只有一个)作为一个整体作为分隔符——而不是按其任何单个字符:

拆分
# On Windows, split by CRLF only.
# (Would also work on Unix with LF-only text.)
# In PowerShell (Core) 7+ only, .Split([Environment]::NewLine) would be enough.
"one`r`ntwo`r`nthree".Split([string[]] [Environment]::NewLine, [StringSplitOptions]::None) |
  ForEach-Object { "[$_]" }

输出:

[one]
[two]
[three]

虽然这显然比使用 -split '\r?\n' 更正式,但它确实具有性能更好的优势 - 尽管这几乎无关紧要。有关此方法的概括,请参阅下一节。


使用明确的.Split()要求提高性能:

注:

  • 只有在 -split '\r?\n'-split '\r?\n|\r' 在实践中 太慢 时才有必要,这种情况不会经常发生。

  • 为了使这项工作稳健地工作,在两个 PowerShell 版本以及长期版本中,您必须小心匹配感兴趣的 .Split() 重载 .

    的确切数据类型
  • 下面的命令等同于-split '\r?\n|\r',即匹配 CRLF、LF 和 CR 换行符。调整字符串数组以进行更严格的匹配。

# Works on both Unix and Windows, in both PowerShell editions
"one`r`ntwo`nthree`rfour".Split(
  [string[]] ("`r`n", "`n", "`r"),
  [StringSplitOptions]::None
) | ForEach-Object { "[$_]" }