如何让Select-Object返回原始类型(例如String)而不是PSCustomObject?

时间:2009-03-06 12:49:25

标签: powershell pscustomobject

以下代码为我提供了一个PSCustomObjects数组,如何让它返回一个字符串数组?

$files = Get-ChildItem $directory -Recurse | Select-Object FullName | Where-Object {!($_.psiscontainer)}

(作为第二个问题,什么是psiscontainer部分?我从网上的例子中复制了它)

接受后编辑:两个很棒的答案,希望我能同时标记它们。已经给出了原始答案。

4 个答案:

答案 0 :(得分:34)

您只需从对象中挑选出您想要的属性即可。在这种情况下FullName

$files = Get-ChildItem $directory -Recurse | Select-Object FullName | Where-Object {!($_.psiscontainer)} | foreach {$_.FullName}

编辑:Mark的解释,他问道:“foreach做了什么?枚举的内容是什么?”

Sung Meister的解释非常好,但我会在这里添加一个演练,因为它可能会有所帮助。

关键概念是管道。想象一系列乒乓球一个接一个地滚下一根细管。这些是管道中的对象。管道的每个阶段 - 由管道(|)字符分隔的代码段 - 都有一个管道进入它,管道从它出来。一级的输出连接到下一级的输入。每个阶段在对象到达时对其进行处理,对它们执行操作,并将它们发送回输出管道或发送新的替换对象。

Get-ChildItem $directory -Recurse

Get-ChildItem遍历文件系统,创建FileSystemInfo对象,代表它遇到的每个文件和目录,并将它们放入管道。

Select-Object FullName

Select-Object在每个FileSystemInfo对象到达时获取它,从中获取FullName属性(在这种情况下是一个路径),将该属性放入它创建的全新自定义对象中,并将该自定义对象放入管道。

Where-Object {!($_.psiscontainer)}

这是一个过滤器。它需要每个对象,检查它,并将其发回或根据某些条件丢弃它。顺便说一句,这里的代码有一个bug。到达此处的自定义对象没有psiscontainer属性。这个阶段实际上并没有做任何事情。 Sung Meister的代码更好。

foreach {$_.FullName}

Foreach,其长名称为ForEach-Object,在它到达时抓取每个对象,并在此处从中获取FullName属性(字符串)。现在,这里是一个微妙的部分:任何未消耗的值,即未被变量捕获或以某种方式被抑制的值,都会被放入输出管道中。作为一项实验,尝试用以下方法替换该阶段:

foreach {'hello'; $_.FullName; 1; 2; 3}

实际上尝试一下并检查输出。该代码块中有四个值。它们都没有消耗掉。请注意,它们都出现在输出中。现在试试这个:

foreach {'hello'; $_.FullName; $ x = 1; 2; 3}

请注意,其中一个值正由变量捕获。它不会出现在输出管道中。

答案 1 :(得分:21)

要获取文件名的字符串,您可以使用

$files = Get-ChildItem $directory -Recurse | Where-Object {!($_.psiscontainer)} | Select-Object -ExpandProperty FullName

-ExpandProperty参数允许您根据指定属性的类型返回对象。

进一步测试表明,这不适用于V1,但从V2 CTP3开始,该功能已得到修复。

答案 2 :(得分:8)

问题#1

我删除了“select-object”部分 - 它是多余的并且在“foreach”之前移动“where”过滤器而不像dangph's answer - 尽快过滤,这样你只处理你所拥有的一部分内容处理下一条管道。

$files = Get-ChildItem $directory -Recurse | Where-Object {!$_.PsIsContainer} | foreach {$_.FullName}

该代码段基本上是

  • 递归获取所有文件的所有文件的完整路径(Get-ChildItem $ directory -Recurse)
  • 过滤目录(Where-Object {!$ _。PsIsContainer})
  • 仅返回完整文件名(foreach {$ _.FullName})
  • 将所有文件名保存到$ files

请注意,对于 foreach {$ _。FullName} ,在powershell中,返回脚本块({...})中的最后一个语句,在本例中为$ _。FullName of type string

如果你真的需要获得一个原始对象,那么在删除“select-object”之后你不需要做任何事情。如果您要使用Select-Object但想要访问原始对象,请使用“PsBase”,这是一个完全不同的问题(主题) - 有关该主题的更多信息,请参阅“What's up with PSBASE, PSEXTENDED, PSADAPTED, and PSOBJECT?

问题#2

并且还通过过滤!$ _。PsIsContainer 意味着您要排除容器级别对象 - 在您的情况下,您在 Get-ChildItem > FileSystem 提供程序(您可以通过Get-PsProvider查看PowerShell提供程序),因此容器是DirectoryInfo(文件夹)

PsIsContainer 在不同的PowerShell提供商下意味着不同的东西; 例如)对于 Registry 提供程序,PsIsContainer的类型为Microsoft.Win32.RegistryKey 试试这个:

>pushd HKLM:\SOFTWARE
>ls | gm

[更新] 以下问题: foreach做了什么?什么是枚举? 为了澄清,“foreach”是“Foreach-Object”的别名 你可以找到,

get-help foreach

- 或 -

get-alias foreach

现在在我的回答中,“foreach”枚举了从前一个管道返回的类型FileInfo的每个对象实例(具有已过滤的目录)。 FileInfo 有一个名为 FullName 的属性,这就是“foreach”所枚举的内容。
并且通过名为“$ _”的特殊管道变量引用通过管道传递的对象,该变量在“foreach”的脚本块上下文中是 FileInfo 类型。

答案 3 :(得分:4)

对于V1,将以下过滤器添加到您的个人资料中:

filter Get-PropertyValue([string]$name) { $_.$name }

然后你可以这样做:

gci . -r | ?{!$_.psiscontainer} | Get-PropertyName fullname

顺便说一句,如果你使用的是PowerShell Community Extensions,那么你已经拥有了这个。

关于在V2中使用Select-Object -Expand的能力,这是一个可爱的技巧但不明显,实际上不是Select-Object和-Expand的意思。 -Expand就像LINQ的SelectMany一样扁平化,Select-Object是关于将多个属性投影到自定义对象上。