什么构成了 Powershell 中 Select-String 方法的 "line"?

What constitutes a "line" for Select-String method in Powershell?

我希望 Select-String 考虑 \r\n(回车-return + 换行符)Powershell 中一行的结尾。

但是,如下所示,abc匹配整个输入:

PS C:\Tools\hashcat> "abc`r`ndef" | Select-String -Pattern "abc"

abc
def

如果我将字符串分成两部分,则 Select-String 的行为与我预期的一样:

PS C:\Tools\hashcat> "abc", "def" | Select-String -Pattern "abc"

abc

如何给 Select-String 一个以 \r\n 结尾的字符串,然后使此 cmdlet 仅 return 那些包含匹配项的字符串?

"abc`r`ndef"

是一个字符串,如果您在控制台中回显 (Write-Output) 将导致:

PS C:\Users\gpunktschmitz> echo "abc`r`ndef"
abc
def

Select-String 将回显每个包含 "abc" 的字符串。由于 "abc" 是字符串的一部分,因此这个字符串将被 selected.

"abc", "def"

是两个字符串的列表。在此处使用 Select-String 将首先测试 "abc" 然后 "def" 如果模式匹配 "abc"。因为只有第一个匹配,所以它将被 selected。

使用以下将字符串拆分为列表,select 仅包含 "abc"

的元素
"abc`r`ndef".Split("`r`n") | Select-String -Pattern "abc"

Guenther Schmitz 先生基本上解释了 Select-String 的正确用法,但我想补充一些观点来支持他的回答。

  1. 我针对这个 Select-String cmdlet 做了一些逆向工程工作。它在 Microsoft.PowerShell.Utility.dll 中。一些相关的代码片段如下,注意这些是逆向工程的代码,仅供参考,并非实际源代码。

    string text = inputObject.BaseObject as string;
    ...
    matchInfo = (inputObject.BaseObject as MatchInfo);
    object operand = ((object)matchInfo) ?? ((object)inputObject);
    flag2 = doMatch(operand, out matchInfo2, out text);
    

    我们可以发现它只是把inputObject当做一个完整的字符串,并没有做任何分割。

  2. 我在 github 上没有找到这个 cmdlet 的实际源代码,可能这个实用程序部分还没有开源。但是我找到了这个Select-Stringunit test

    $testinputone = "hello","Hello","goodbye"
    $testinputtwo = "hello","Hello"
    

    他们用于单元测试的测试字符串实际上是字符串列表。这意味着他们甚至没有考虑您的用例,很可能它只是为了接受字符串集合的输入而设计的。

  3. 但是,如果我们看一下 Microsoft 关于 Select-Stringofficial document,我们确实看到它在谈论 line 很多,而它无法识别字符串中的 line。我个人的猜测是 line 的概念只有在 cmdlet 接受文件作为输入时才有意义,在这种情况下文件就像一个字符串列表,列表中的每个项目代表一个单行.

希望它能让事情更清楚。

  • Select-String 对每个对象进行操作(按需字符串化[1]输入对象.

  • 一个多行字符串如"abc`r`ndef"是一个输入对象。

    • 相比之下,"abc", "def" 是一个包含两个元素的字符串 array,作为 two 输入对象传递。
  • 为了确保多行字符串的单独传递,将字符串拆分成一个行数组 使用 PowerShell 的 -split 运算符:"abc`r`ndef" -split "`r?`n"

    • ? 使 `r 可选,以便也正确处理 `n-only(LF-only,Unix 风格)行尾。)

简而言之:

"abc`r`ndef" -split "`r?`n" | Select-String -Pattern "abc"

等效的,使用带有正则表达式 (regex) 转义序列的 PowerShell 字符串文字(-split 的 RHS 是一个正则表达式):

"abc`r`ndef" -split '\r?\n' | Select-String -Pattern "abc"

有点不幸的是,Select-String 文档讨论了对 文本进行操作,因为真正的操作单位是输入 对象 - 正如我们所见,它们本身可能包含多行。
据推测,这来自 典型 通过 Get-Content cmdlet 提供输入对象的用例,它输出文本文件的行 一个接一个 .

请注意,Select-String 不会 return 直接 匹配字符串 ,而是将它们包装在 [Microsoft.PowerShell.Commands.MatchInfo] 对象中,其中包含有关比赛。 然而,即使在那里存在行隐喻,因为它是 .Line 属性 包含匹配的 string.


[1] 可选阅读:Select-String 如何将输入对象字符串化

如果输入对象还不是字符串,则会将其转换为字符串,但可能不是您预期的方式:

松散地说,在每个非字符串输入对象上调用.ToString()方法[2] ,对于非字符串 not 与您使用 PowerShell 的 默认输出格式 获得的表示相同(后者是您将对象打印到 控制台 或使用 Out-File 时看到的内容);相比之下,它 与在双引号字符串中使用 字符串插值 得到的表示相同(当您在 "...",例如,"$HOME""$(Get-Date)").

通常,.ToString() 只生成对象的 类型 的名称,不包含任何特定于实例的信息;例如,$PSVersionTable 字符串化为 System.Management.Automation.PSVersionHashTable.

# Matches NOTHING, because Select-String sees
# 'System.Management.Automation.PSVersionHashTable' as its input.
$PSVersionTable | Select-String PSVersion 

如果您想要逐行搜索默认输出格式,请使用以下习惯用法:

... | Out-String -Stream | Select-String ...

但是,请注意,对于非字符串输入,后续处理通过 查询属性Where-Object 条件.

就是说,Select-String 需要 隐式 应用 Out-String -Stream 字符串化,正如所讨论的在 this GitHub feature request.


[2] 更准确地说,.psobject.ToString() 会按原样调用,或者 - 如果对象的 ToString 方法支持 IFormatProvider 类型的参数- 作为 .psobject.ToString([cultureinfo]::InvariantCulture) 以获得 文化不变 表示 - 请参阅 this answer 了解更多信息。