使用PowerShell sls (Select-String)与grep和findstr的比较

10
请问有人能解释一下 sls(Select-String)与 grep 和 findstr 的区别吗?
grep: grep <pattern> files.txt sls: sls <pattern> files.txt(默认参数位置为模式,然后是文件)
grep 示例:grep "search text" *.logcat *.log | grep "search text" sls 示例:sls "search text" *.logcat *.log | grep "search text" 另外,所有 PowerShell 命令都不区分大小写,而 Linux 工具通常区分大小写,而像 findstr 这样的旧工具也是区分大小写的,但 findstr 可以在 PowerShell 中使用,并且适用于 sls 不适用的情况,例如:Get-Service | findstr "Sec" (这可以正常工作!),但是当我们尝试以类似的方式使用 sls 时 Get-Service | sls "Sec" 我们什么都没有得到(可能是因为 sls 使用字符串,但 Get-Service 返回一个对象,所以这是可以理解的,但是 findstr 在做什么,它怎么能将输出视为字符串呢?)。
所以,我的想法是“好的,我需要将 Get-Service 输出的内容转换为字符串来使用 PowerShell 命令”,但这并不起作用(或者不是我期望的方式): Get-Service | Out-String | sls "Sec"(给出结果,但很奇怪) (Get-Service).ToString() | sls "Sec"(.ToString()只返回“System.Object[]”)
通常我应该如何将对象转换为字符串,以便它可以操作信息(就像 Get-Service | findstr "Sec" 可以轻松地执行操作一样)?
如果有人能解释一下上述内容是如何组合在一起的,那么我会更好地利用 sls。特别是,Get-Service | Out-String | sls "Sec" 确实返回了东西,但不是我期望的东西(如果它正在搜索“s”和“e”和“c”的每个字符,那么它就会返回很多内容,如果是这样的话,那么这样做并不是很直观)。
2个回答

11

当您默认使用 Out-String 时,它会将管道输入对象(在此情况下为服务对象数组)转换为单个字符串。幸运的是,-Stream 开关允许每行输出作为单个字符串。关于区分大小写,Select-String 支持 -CaseSensitive 开关。

# For case-insensitive regex match
Get-Service | Out-String -Stream | Select-String "Sec"

# For case-sensitive regex match
Get-Service | Out-String -Stream | Select-String "Sec" -CaseSensitive

# For case-sensitive non-regex match
Get-Service | Out-String -Stream | Select-String "Sec" -CaseSensitive -SimpleMatch

无论哪种情况,Select-String 使用正则表达式(使用 -SimpleMatch 开关进行字符串匹配)来对每个输入字符串进行模式匹配,并输出与模式匹配的整个字符串。因此,如果您只将包含多行的单个字符串传输到它中,那么在成功匹配时所有行都将被返回。


太好了,感谢澄清。这解决了我长期以来一直不理解的问题。很有道理。我在谷歌上搜索了一下,但没有找到这个。我知道CaseSensitive开关,但不知道SimpleMatch,这也非常有用。有了这些信息,将来使用Select-String会非常容易。 - YorSubs
1
然而,我仍然有一个疑问:在findstr情况下会发生什么?当我们将其管道传递到非Cmdlet时,PowerShell是否会说“啊,这个findstr不是Cmdlet,所以在传递之前执行Out-String -Stream”因为它似乎将每一行作为单个字符串传递给findstr,对吗? - YorSubs
1
+1 对于答案,但是评论中的 findstr 解释令人困惑。实际上,当将数据传输到外部程序时,PowerShell 隐式 暗示了 Out-String -Stream,对于非字符串输入到 Select-Object 也应该这样做(请参见 https://github.com/PowerShell/PowerShell/issues/10726)。也就是说,PowerShell 将有效地 Out-String -Stream 字符串化输入的每一行提供给 findstr,并且 PowerShell 不可避免地以换行符终止每个字符串化的输入(行)。/cc @YorSubs。 - mklement0

4

为了补充AdminOfThings'有帮助的回答:

  • 为了在非字符串输入对象的用于显示字符串表示中找到字符串,就像它们会在控制台上打印一样,确实需要将其管道传输到Out-String -Stream,而默认情况下,应用简单的.ToString()字符串化[1]

    • 但是你不需要手动这样做:如GitHub issue #10726所建议的那样,Select-String应该隐式地执行它。
  • 有趣的是,当管道传输到外部程序(如findstr.exe)时,PowerShell已经隐式地应用了Out-String -Stream;例如:

    • Get-Date 1/1/2019 | findstr January有效(在以en为基础的文化中),因为它隐含地相当于
      Get-Date 1/1/2019 | Out-String -Stream | findstr January
    • 相比之下,Get-Date 1/1/2019 | Select-String January相当于
      (Get-Date 1/1/2019).ToString([cultureinfo]::InvariantCulture) | Select-String January,因此不起作用,因为输入求值为01/01/2019 00:00:00

[1] 更准确地说,会调用 .psobject.ToString() 方法,如果该对象的ToString方法支持一个IFormatProvider类型的参数,则会以 .psobject.ToString([cultureinfo]::InvariantCulture) 的形式进行调用,以获取不受语言文化影响的表示 - 有关更多信息,请参见 此答案


1
实际上,我觉得很奇怪的是,当传递给findstr时,这种情况会隐式发生,但对于Select-String却不会(我认为没有理由不这样做)。通常有更深层次的技术原因(坦白地说,我通常不理解),但看起来如果Select-String隐式执行与管道到findstr相同的Out-String -Stream操作,它的行为会更好。过去我一直觉得使用Select-String很麻烦(所以我只是避免使用它),并且从未理解过原因。感谢您的解释(希望提出的更改能够实施)。 - YorSubs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接