在每行末尾拆分文本



我有一个脚本,它在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中,拆分没有任何作用,并且CCD_;不存在";。

在Powershell 7中,换行处理方式不同(至少我认为是这样),但我找不到解决方案。

我尝试将代码更改为$rows = $path.split([Environment]::NewLine)$path.Split([System.Environment]::NewLine, [System.StringSplitOptions]::RemoveEmptyEntries),但这不会改变任何事情。。

此外,当我使用";此处字符串";

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

$x[0]外的所有内容均为空(即$x[2])

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

这里:powershell-split(';';)指定一个新行

这里:WT:在不执行的情况下将多行粘贴到Windows终端

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

感谢您的帮助。

编辑:我发现了关于这个问题的提示,但还不明白它的含义: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[]]"rn") # doesnt' work in 7.2.1
$splits = $test.Split([char[]]"`r`n") # works in 7.2.1

tl;dr

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

  • 如果您还想只处理CR换行(这可能很不寻常,但对您来说似乎是这样),请使用-split 'r?n|r'

  • 在Windows上,仅使用CRLF换行符,.Split([Environment]::NewLine)仅在PowerShell(Core)7+中按预期工作,而不是在Windows PowerShell(而且,意外地,在仅使用CR换行符,就像在您的情况下一样。)若要仅通过CR显式拆分,.Split("`r")将恰好在两个版本中都按预期工作,由于仅通过单个字符进行拆分。

# 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运算符,正则表达式进行操作,从而启用上面显示的灵活匹配逻辑。

    • 此regex101.com页面解释了r?n|rregex,并允许您试用它
  • 相比之下,System.String.Split().NET方法仅通过文字字符串进行拆分,这虽然更快,但限制了您只能逐字查找分隔符。

  • 语法含义如下:

    • Regex构造(如转义序列r(CR)和n(LF))仅受.NET正则表达式引擎支持,因此仅受-split(以及使用正则表达式的其他PowerShell上下文)支持;regex元字符?(与前面的子表达式零次或一次匹配)和|(交替;与两边的子表达式匹配)也是如此
      字符串内部(这是正则表达式在PowerShell中必须表示的方式,最好是在'...'内部),这些序列和字符对本身和.Split()方法都没有特殊意义,后者逐字逐句地处理所有

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

  • 这个答案更深入地讨论了-split.Split(),并建议常规使用-split


至于您尝试了什么

  • 如果确定输入字符串使用平台本机换行格式,则仅使用$x[1]0。值得注意的是,在PowerShell提示符下交互式输入的多行字符串文字即使在Windows上也使用Unix格式的LF换行符(唯一的例外是过时的仅限Windows的ISE,它使用CRLF)。

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

  • 此外,正如您在自己的回答中所暗示的,在.NET Core/.NET 5+-中的System.String.Split()方法中添加string参数重载,因此PowerShell(Core)v6+-隐式地导致了相对于Windows PowerShell的中断性更改:具体而言,.Split('ab')Windows PowerShell中由'a''b'分割,即由组成字符串的任何单个字符分割,而在PowerShell(Core)v6+中由整个字符串'ab'分割。

    • 这样的隐式中断更改很少见,但它们确实发生了,而且超出了PowerShell的控制范围。

    • 因此,您应该始终更喜欢PowerShell本机功能以获得长期稳定性,在这种情况下,这意味着更喜欢-split运算符而不是.Split().NET方法

      • 也就是说,有时由于性能原因,.NET方法更可取可以使它们稳健地工作,但前提是仔细匹配感兴趣的方法重载的确切数据类型,这可能需要强制执行;请参见下文
    • 请参阅此答案以获取更多信息,包括对隐式断裂变化的更详细解释。

您对-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 { "[$_]" }

原因:在将文本粘贴到终端时,使用的终端很重要。默认的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仅使用换行符n字符分隔新行

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

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


解决方案:在上拆分rnn上的多行字符串(但不是仅在r上)。这里的简单方法是使用像@Santiago squarzon建议的regex:

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

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

在.NET4中,字符串类只有将字符作为参数类型。PowerShell看到它并自动转换它,让你的生活轻松一点。注意有一个隐含的"OR"(|)这里是一个字符数组。

为什么PowerShell 7的行为不同?在.NET5中,字符串类具有一些接受字符串的附加参数。PowerShell 7确实不采取任何自动行动。

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

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

相关内容

  • 没有找到相关文章

最新更新