43

下面的代码给了我一个 PSCustomObjects 数组,我怎样才能让它返回一个字符串数组?

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

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

接受后编辑:两个很好的答案,希望我能同时标记它们。已授予原始答案。

4

4 回答 4

40

您只需要从对象中挑选出您想要的属性。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)}

这是一个过滤器。它获取每个对象,对其进行检查,然后根据某些条件将其送回或丢弃。顺便说一句,您这里的代码有一个错误。到达这里的自定义对象没有 psiscontainer 属性。这个阶段实际上并没有做任何事情。Sung Meister 的代码更好。

foreach {$_.FullName}

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

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

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

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

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

于 2009-03-06T13:06:24.637 回答
30

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

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

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

进一步的测试表明,这不适用于 V1,但该功能自 V2 CTP3 起已修复。

于 2009-03-07T04:32:46.470 回答
9

对于问题 #1

我已经删除了“select-object”部分 - 它是多余的,并且在“foreach”之前移动了“where”过滤器,这与dangph 的答案不同- 尽快过滤,以便您只处理下一个必须处理的子集管道。

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

该代码片段基本上读取

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

请注意,对于foreach {$_.FullName},在 powershell 中,将返回脚本块 ({...}) 中的最后一条语句,在本例中为 $_.FullName 类型的字符串

如果你真的需要得到一个原始对象,你不需要在摆脱“select-object”之后做任何事情。如果您要使用 Select-Object 但想要访问原始对象,请使用“PsBase”,这是一个完全不同的问题(主题) - 请参阅“ PSBASE、PSEXTENDED、PSADAPTED 和 PSOBJECT 有什么问题? ”了解更多信息那个主题

对于问题 #2

并且通过!$_.PsIsContainer过滤意味着您排除了容器级别的对象 - 在您的情况下,您正在对FileSystem提供程序执行Get-ChildItem(您可以通过 Get-PsProvider 查看 PowerShell 提供程序),因此容器是目录信息(文件夹)

PsIsContainer在不同的 PowerShell 提供者下意味着不同的东西;例如)对于注册表提供程序,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类型。

于 2009-03-06T13:39:18.740 回答
3

对于 V1,将以下过滤器添加到您的配置文件中:

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

然后你可以这样做:

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

顺便说一句,如果您使用的是PowerShell 社区扩展,那么您已经拥有它。

关于在 V2 中使用 Select-Object -Expand 的能力,这是一个可爱的技巧,但并不明显,而且真的不是 Select-Object 和 -Expand 的本意。-Expand 与 LINQ 的 SelectMany 一样是关于展平的,而 Select-Object 则是关于将多个属性投影到自定义对象上。

于 2009-03-22T06:45:41.607 回答