【问题标题】:Split text after each end of line [duplicate]在每行末尾拆分文本[重复]
【发布时间】:2021-12-16 13:36:55
【问题描述】:

我的脚本在 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

在这里:powershell -split('') specify a new line

在这里:WT: Paste multiple lines to Windows Terminal without executing

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

感谢任何帮助。

编辑:我发现了关于这个问题的提示,但还不明白它的含义: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

【问题讨论】:

  • 您的第一个示例在 PS Core (Linux) 上运行良好。在你的第二个例子中尝试使用-split "\r?\n"
  • 感谢您的回答。我在 Windows 10 (20H2) 上。 “`r`n”也有同样的问题
  • 您的第一个示例在 PS7 上对我不起作用,但如果我使用 $test.Split("`n"),它确实有效。我认为其中一些行为会根据您的主机环境而有所不同(我的是 windows)
  • -split "\r?\n" 应该适用于所有情况,无论是 Windows / Linux / mac / PS Core / Win PS。
  • 我明白了。如果您确实有 CRLF 换行符,请注意您自己答案中的解决方案以及最新编辑中的 $test.Split([char[]]"`r`n") 将无法按预期工作,因为您将获得额外的空元素,表示每个 CR 和 LF 之间的空字符串序列,由于匹配这些字符。 个别。您的最新编辑还显示了关于您可以在哪里使用哪些转义序列以及在逐字与可扩展字符串中使用转义序列的误解 - 请参阅我的答案的更新。 /cc @SantiagoSquarzon

标签: powershell powershell-core


【解决方案1】:

tl;dr:

  • 使用 -split '\r?\n 将多行文本拆分成行无论是使用 Windows 格式的 CRLF 还是 Unix 格式的 LF 换行符(它甚至可以处理 将这些格式混合在一个字符串中)。

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

  • 在 Windows 上,只有 CRLF 换行符,.Split([Environment]::NewLine) 只能在 PowerShell (Core) 7+ 中按预期工作,而不是在 Windows PowerShell 中(并且,偶然地,在Windows PowerShell only 与 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]

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

注意:

  • 上面使用了PowerShell的-split operator,在regexes (regular expressions)上运行,可以实现上面所示的灵活匹配逻辑。

  • 相比之下,System.String.Split().NET 方法仅按文字字符串进行分割,而更快,限制您查找 逐字 分隔符。

  • 语法的含义是:

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

    • 相比之下,类似的转义序列 "`r" (CR) 和 "`n" (LF) 是 PowerShell 功能,在 expandable strings 中可用,即它们 仅在 "..." 内工作 - 不在 verbatim strings、'...' 内工作 - 并扩展到它们代表的字符在目标运算符之前 、方法或命令会看到结果字符串。

  • This answer 更深入地讨论了-split 与.Split(),并建议日常使用-split。


至于你尝试了什么:

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

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

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

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

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

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

您对 -split '\r?\n' 的反馈对您不起作用,your own answer 中的解决方案表明您的输入字符串 - 不寻常 - 使用 CR-only 换行符。

您的答案的解决方案不会对 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 { "[$_]" }

【讨论】:

    【解决方案2】:

    原因:将文本粘贴到终端时,您使用的终端很重要。默认的 powershell 5.1、ISE 终端和大多数其他 Windows 软件使用回车符 \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 用 only 换行符 \n 字符分隔换行符

    # 7.2 Core
    $test = "a
    b
    c"
    [byte[]][char[]]$test -join ','
    
    97,10,98,10,99
    

    在 Windows 操作系统上,[Environment]::NewLine 是 \r\n,无论是哪个控制台。在 Linux 上,它是\n。


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

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

    【讨论】:

    • 会投票,因为我同意你的回答,但仍然觉得应该为我链接的副本关闭问题。只是OP拒绝尝试给他的选项。
    • @Santiago,它大部分是重复的,是的,但我认为有两个方面值得单独解决:[Environment]::NewLine 的使用和事​​实 - 就我可以告诉 - OP 有 CR-only 换行符(这是不寻常的),所以通过扩展 \r?\n 正则表达式来展示如何处理这些是值得的。
    • @mklement0 诚实的问题,除非专门使用 `r ,否则如何在 Windows 上创建仅包含回车且没有换行符的 here-string?
    • @Cpt.Whale:请注意,只有(过时的)ISE 在交互式提示下使用 CRLF; 终端(常规控制台窗口、Windows 终端、VSCode 中的集成终端)仅在 Windows PowerShell 和 PowerShell(核心)中使用 LF - both。 Quibble:将[char] 实例转换为[int] 比[byte] 更好-后者将与代码点高于0xff 的Unicode 字符中断(例如[byte[]] [char[]] '⚠️')
    • @SantiagoSquarzon,实现这一目标的唯一方法是使用仅 CR 换行符保存封闭的脚本文件(如果您的编辑器允许)。绝对不寻常(仅 CR 文件可能由遗留应用程序生成,包括 pre-OSX/macOS Mac 应用程序),但根据 OP 的反馈,我认为这是唯一合理的解释。
    【解决方案3】:

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

    在 .NET 4 中,字符串类只有将字符作为 参数类型。 PowerShell 看到并自动转换它, 让您的生活更轻松。注意有一个隐含的“或”(|) 这里是一个字符数组。

    为什么 PowerShell 7 表现不同?在 .NET 5 中,字符串类 有一些接受字符串的附加参数。 PowerShell 7 可以 不采取任何自动操作。

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

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

    【讨论】:

    • 您显然没有尝试@SantiagoSquarzon 评论-split '\r?\n'.. 使用正则表达式,其中问号表示在 CRLF 上拆分,或者如果没有 CR,则仅在 LF 上拆分..
    • 你是对的,我没有尝试他的答案。但是我刚刚尝试过,这不是解决方案..它不起作用..仍然x[1]为空...使用我发布的解决方案,它在Powershell 5和Powershell 7中都可以使用...
    • @DanStef "not work" 在这种情况下仅适用于您,该正则表达式适用于其他任何人,无论他们的操作系统或 PS 版本如何。因此,您的问题无法重现。
    • @Dan 您的答案将在具有 windows 行尾的字符串上拆分两次:\r\n,导致行尾格式之间的结果不同。根据您的用例,这可能无关紧要。圣地亚哥的答案更好,或者您可以使用-split '\r\n|\n'(相同,但更长)。
    猜你喜欢
    • 2013-04-05
    • 2016-11-02
    • 1970-01-01
    • 2018-04-08
    • 2020-06-22
    • 1970-01-01
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    相关资源
    最近更新 更多