什么构成Powershell中Select-String方法的“线”?

时间:2018-04-22 09:57:02

标签: powershell split select-string

我希望Select-String\r\n(回车+换行符)视为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仅返回包含匹配项的字符串?

3 个答案:

答案 0 :(得分:3)

  • Select-String对每个(按字母顺序排列的 [1] 输入对象进行操作。

  • 多行字符串(例如"abc`r`ndef")是单个输入对象。

    • 相比之下,"abc", "def"是一个带有两个元素的字符串 array ,作为两个输入对象传递。
  • 要确保多行字符串的分别传递,请将字符串拆分为行数使用PowerShell的-split运算符:"abc`r`ndef" -split "`r?`n"

    • ?使`r成为可选项,以便正确处理`n - 仅限(仅限LF,Unix风格)行结尾。)

简而言之:

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

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

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

有些不幸的是Select-String文档谈到了对文本的进行操作,因为真正的操作单元是输入对象 - 这可能是正如我们所见,它们本身包含多条线 据推测,这来自于通过Get-Content cmdlet提供输入对象的典型用例,它逐个输出文本文件的行

请注意,Select-String不会直接返回匹配字符串 ,而是将它们包含在[Microsoft.PowerShell.Commands.MatchInfo]个对象中,这些对象包含有关匹配的有用元数据。 即使存在行隐喻,因为.Line属性包含匹配的字符串

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

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

松散地说,在每个非字符串输入对象上调用.ToString()方法 [2] ,对于非字符串 与使用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条件的输入。

[2]更准确地说,.psobject.ToString()被调用,或者是 - 如果对象的ToString方法支持IFormatProvider - 类型的参数 - 作为{{ 1}}以便获得文化不变的表示 - 请参阅我的this answer作为背景。

答案 1 :(得分:2)

"abc`r`ndef"

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

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

Select-String将回显出“abc”所属的每个字符串。由于“abc”是字符串的一部分,因此将选择此字符串。

"abc", "def"

是两个字符串的列表。在这里使用Select-String将首先测试“abc”,然后如果模式匹配“abc”则测试“def”。由于只有第一个匹配,它才会被选中。

使用以下内容将字符串拆分为列表,并仅选择包含“abc”

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

答案 2 :(得分:1)

基本上,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-String的{​​{3}}。

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

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

  3. 但是,如果我们看一下微软的unit test关于Select-String我们确实看到它谈论很多而它无法识别字符串在一个字符串中。我的个人猜测是的概念仅在cmdlet接受文件作为输入时才有意义,如果文件类似于字符串列表,则列表中的每个项目代表一行。

  4. 希望它能让事情更清楚。