PowerShell: 如何对命令输出进行grep操作?

109

PowerShell 中,我尝试过:

alias | select-string Alias

尽管输出中明显包含Alias,但此方法仍然失败。我知道这是因为select-string正在操作某个对象而不是实际的输出字符串。

有什么解决办法吗?

8个回答

118

我认为这个解决方案更加容易和更好,直接使用函数findstr:

alias | findstr -i Write

你还可以创建一个别名来使用grep命令:

new-alias grep findstr

5
这是所有答案中最直观和易于记忆的方法。 - o.v
12
为了澄清两件事情,因为我在理解这个命令的操作时有些困惑。首先是“-i”。如果你使用“findstr /?”查看帮助信息,它将显示所有选项,并在前面加上一个斜杠“/”。但是,它也接受以斜杠“/”或减号“-”为前缀的参数。例如,“ipconfig /all”与“ipconfig -all”具有相同的功能。所以,“-i”等同于“/i”,由于findstr是一个旧的DOS程序,而旧的DOS程序不区分大小写,因此“/i”与“/I”相同。因此,“-i”表示进行一次不区分大小写的搜索。 - Stack of Pancakes
6
接下来困惑我的是结尾处的 WritealiasGet-Alias 的别名,它是 PowerShell 的一个命令。而 Write 实际上是被搜索的字符串,不是我最初因为 PowerShell 命令的彩色格式而认为的 Write-Output 的别名。我曾经以为 Write-Output 被原地替换以供 findstr 搜索。但实际情况并非如此。每一行 Get-Alias 的输出都会被管道传递到 findstr 中,然后 findstr 将在这些字符串中搜索大小写不敏感的搜索词 "Write"。 - Stack of Pancakes
非常适合我的需求! - Marinaio
1
这是一个 cmd.exe/DOS 的答案,但问题中提到了 powershell - c z
“findstr” 的问题在于行长度限制(8191 个字符)。 - golimar

74

你的问题在于alias会生成一系列AliasInfo对象,而不是一系列字符串。以下代码可以解决你的问题。

alias | out-string -stream | select-string Alias

或者作为一个函数

function grep {
  $input | out-string -stream | select-string $args
}

alias | grep Alias

当你不处理管道中的东西时(比如只运行'alias'命令),shell会使用每个对象的ToString()方法(或者使用ETS信息中指定的输出格式)。


1
只是想补充一下,这适用于来自通用命令行程序的输出。它模仿了Linux中的./foo | grep bar,就像我想要的那样。 - Sint
这是我正在寻找的内容:Get-Command | out-string -stream | select-string VerboseFunction Disable-DeliveryOptimizationVerboseLogs 1.0.2.0 DeliveryOptimization Function Enable-DeliveryOptimizationVerboseLogs 1.0.2.0 DeliveryOptimization Cmdlet Write-Verbose 7.0.0.0 Microsoft.PowerShell.Utility - zBernie
感谢上帝,有人发了一篇文章。从Unix背景来看,处理PowerShell对象真的很让人烦恼。 - Adam D.

36

如果您真正想要“grep”格式化的输出(显示字符串),那么请使用Mike的方法。 有时这很方便。但是,如果您想尝试拥抱PowerShell的对象管道本质,请尝试以下方法。首先,请查看流经管道的对象的属性:

PS> alias | Get-Member


   TypeName: System.Management.Automation.AliasInfo

Name                MemberType     Definition
----                ----------     ----------
Equals              Method         bool Equals(System.Object obj)
GetHashCode         Method         int GetHashCode()
GetType             Method         type GetType()
ToString            Method         string ToString()
<snip>
*Definition*        Property       System.String Definition {get;}
<snip>
请注意Definition属性,这是当您显示Get-Alias(别名)的输出时看到的标题,例如:
PS> alias

CommandType     Name           *Definition*
-----------     ----           ----------
Alias           %              ForEach-Object
<snip>

通常标题名称与属性名称匹配,但并非总是如此。这就是使用 Get-Member 的好处所在。它会向您展示需要“编写脚本”的内容。现在,如果您想要“过滤”的是 Definition 属性内容,则可以考虑使用以下方法。不仅可以过滤该属性的值,而且还可以通过流水线中的每个 AliasInfo 对象来过滤此属性的内容,并且可以使用正则表达式进行过滤。

PS> alias | Where-Object {$_.Definition -match 'alias'}

CommandType     Name                   Definition
-----------     ----                   ----------
Alias           epal                   Export-Alias
Alias           gal                    Get-Alias
Alias           ipal                   Import-Alias
Alias           nal                    New-Alias
Alias           sal                    Set-Alias

在这个例子中,我使用 Where-Object cmdlet 根据某些任意脚本过滤对象。在这种情况下,我根据 Defintion 属性与正则表达式 "alias" 匹配来进行过滤。只有那些对该筛选器返回 true 的对象才允许向下传播并被格式化以在主机上显示。

顺便说一句,如果您正在键入此命令,则可以使用 Where-Object 的两个别名之一 -“Where”或“?”。例如:

PS> gal | ?{$_.Definition -match '-Item*'}

5
你也可以使用findstr命令,由于它不是一个cmdlet而是一个程序,因此PowerShell将为您处理文本转换(仅供完整性;总的来说,根据属性过滤数据通常是更好的选择)。 - Joey
根据PowerShell 3,以下更为简洁: gal | Where Definition -match 'alias' - tkokasih
我认为这个答案回应了帖子的内容,但并不符合标题。如果我的命令是(例如)"pip freeze" - 输出只是文本。Roi的答案才是这个标题所指的意思。 - Danny Staple

16

有两个问题。如同问题所述,select-string需要在输出字符串上操作,可以使用"out-string"获取。另外,select-string无法对传递给它的字符串进行逐行操作。以下是通用解决方案。

(alias|out-string) -split "`n" | select-string Write 

4
在PowerShell 1.0中,没有-split选项。你可以尝试以下方法:(alias|out-string -stream) | select-string Write - Mike
1
你们完全错过了重点,不需要使用grep来处理PowerShell的输出。 - x0n
3
@x0n 嗯,有些情况下,当我通过PS控制台快速搜索某些内容时,我并不知道或者并不关心它是否在特定的属性中。我觉得使用Get-Member命令查找正确的属性和表达式,再在where命令中使用太过繁琐。而grep在这种情况下特别好用。 - OnesimusUnbound
4
不需要使用split,只需在Out-String上使用“-Stream”参数,然后就可以直接将其管道传递到Select-String。 - Keith Hill

5
提议的解决方案过于繁琐,我们可以像这样做:
Get-Alias -Definition Write*

3

试试这个:

PS C:\> ipconfig /displaydns | Select-String -Pattern 'www.yahoo.com' -Context 0,7

>     www.yahoo.com
      ----------------------------------------
>     Record Name . . . . . : www.yahoo.com
      Record Type . . . . . : 5
      Time To Live  . . . . : 27
      Data Length . . . . . : 8
      Section . . . . . . . : Answer
      CNAME Record  . . . . : new-fp-shed.wg1.b.yahoo.com

模式字符串默认情况下是一个正则表达式。在其他可选参数中,-CaseSensitive可能很有用,因为它默认情况下是不区分大小写的。 - C Perkins

1
PS> alias | Where-Object {$_.Definition -match 'alias'}

CommandType     Name                   Definition
-----------     ----                   ----------
Alias           epal                   Export-Alias
Alias           gal                    Get-Alias
Alias           ipal                   Import-Alias
Alias           nal                    New-Alias
Alias           sal                    Set-Alias

那么在PS中,与“匹配”相矛盾的是什么?比如获取某列中不匹配特定值的字段。


0

如果想要更灵活和懒惰的解决方案,可以匹配对象的所有属性。大多数情况下,这应该能够得到您想要的行为,而当它不起作用时,您总是可以更具体地指定。以下是一个基于此原则工作的grep函数:

Function Select-ObjectPropertyValues {
    param(
    [Parameter(Mandatory=$true,Position=0)]
    [String]
    $Pattern,
    [Parameter(ValueFromPipeline)]
    $input)

    $input | Where-Object {($_.PSObject.Properties | Where-Object {$_.Value -match $Pattern} | Measure-Object).count -gt 0} | Write-Output
}

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