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

发布于 2024-07-14 19:44:02 字数 303 浏览 12 评论 0原文

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

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

(作为第二个问题,psiscontainer 部分的用途是什么?我从在线示例中复制了该部分)

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

The following code gives me an array of PSCustomObjects, how can I get it to return an array of Strings?

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

(As a secondary question, what's the psiscontainer part for? I copied that from an example online)

Post-Accept Edit: Two great answers, wish I could mark both of them. Have awarded the original answer.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

坐在坟头思考人生 2024-07-21 19:44:02

您只需从对象中挑选出您想要的属性即可。 在本例中为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}

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

You just need to pick out the property you want from the objects. FullName in this case.

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

Edit: Explanation for Mark, who asks, "What does the foreach do? What is that enumerating over?"

Sung Meister's explanation is very good, but I'll add a walkthrough here because it could be helpful.

The key concept is the pipeline. Picture a series of pingpong balls rolling down a narrow tube one after the other. These are the objects in the pipeline. Each stage of pipeline - the code segments separated by pipe (|) characters - has a pipe going into it and pipe going out of it. The output of one stage is connected to the input of the next stage. Each stage takes the objects as they arrive, does things to them, and sends them on into the output pipeline or sends out new, replacement objects.

Get-ChildItem $directory -Recurse

Get-ChildItem walks through the filesystem creating FileSystemInfo objects that represent each file and directory it encounters, and puts them into the pipeline.

Select-Object FullName

Select-Object takes each FileSystemInfo object as it arrives, grabs the FullName property from it (which is a path in this case), puts that property into a brand new custom object it has created, and puts that custom object out into the pipeline.

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

This is a filter. It takes each object, examines it, and sends it back out or discards it depending on some condition. Your code here has a bug, by the way. The custom objects that arrive here don't have a psiscontainer property. This stage doesn't actually do anything. Sung Meister's code is better.

foreach {$_.FullName}

foreach, whose long name is ForEach-Object, grabs each object as it arrives, and here, grabs the FullName property, a string, from it. Now, here is the subtle part: Any value that isn't consumed, that is, isn't captured by a variable or suppressed in some way, is put into the output pipeline. As an experiment, try replacing that stage with this:

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

Actually try it out and examine the output. There are four values in that code block. None of them are consumed. Notice that they all appear in the output. Now try this:

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

Notice that one of the values is being captured by a variable. It doesn't appear in the output pipeline.

白鸥掠海 2024-07-21 19:44:02

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

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

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

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

To get the string for the file name you can use

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

The -ExpandProperty parameter allows you to get back an object based on the type of the property specified.

Further testing shows that this did not work with V1, but that functionality is fixed as of the V2 CTP3.

风追烟花雨 2024-07-21 19:44:02

对于问题#1

我删除了“select-object”部分 - 它是多余的,并将“where”过滤器移到“foreach”之前,这与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 of type string

如果你确实需要获取一个原始对象,那么在去掉“select-object”之后你不需要做任何事情。 如果您要使用 Select-Object 但想要访问原始对象,请使用“PsBase”,这是一个完全不同的问题(主题) - 请参阅“PSBASE、PSEXTENDED、PSADAPTED 和 PSOBJECT 怎么样?”有关该主题的更多信息

对于问题 #2

并且还按 !$_.PsIsContainer 进行过滤意味着您要排除容器级对象 - 在您的情况下,您在 FileSystem 提供程序上执行 Get-ChildItem(您可以通过 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

For Question #1

I have removed "select-object" portion - it's redundant and moved "where" filter before "foreach" unlike dangph's answer - Filter as soon as possible so that you are dealing with only a subset of what you have to deal with in the next pipe line.

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

That code snippet essentially reads

  • Get all files full path of all files recursively (Get-ChildItem $directory -Recurse)
  • Filter out directories (Where-Object {!$_.PsIsContainer})
  • Return full file name only (foreach {$_.FullName})
  • Save all file names into $files

Note that for foreach {$_.FullName}, in powershell, last statement in a script block ({...}) is returned, in this case $_.FullName of type string

If you really need to get a raw object, you don't need to do anything after getting rid of "select-object". If you were to use Select-Object but want to access raw object, use "PsBase", which is a totally different question(topic) - Refer to "What's up with PSBASE, PSEXTENDED, PSADAPTED, and PSOBJECT?" for more information on that subject

For Question #2

And also filtering by !$_.PsIsContainer means that you are excluding a container level objects - In your case, you are doing Get-ChildItem on a FileSystem provider(you can see PowerShell providers through Get-PsProvider), so the container is a DirectoryInfo(folder)

PsIsContainer means different things under different PowerShell providers;
e.g.) For Registry provider, PsIsContainer is of type Microsoft.Win32.RegistryKey
Try this:

>pushd HKLM:\SOFTWARE
>ls | gm

[UPDATE] to following question: What does the foreach do? What is that enumerating over?
To clarify, "foreach" is an alias for "Foreach-Object"
You can find out through,

get-help foreach

-- or --

get-alias foreach

Now in my answer, "foreach" is enumerating each object instance of type FileInfo returned from previous pipe (which has filtered directories). FileInfo has a property called FullName and that is what "foreach" is enumerating over.
And you reference object passed through pipeline through a special pipeline variable called "$_" which is of type FileInfo within the script block context of "foreach".

苏大泽ㄣ 2024-07-21 19:44:02

对于 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 则是关于将多个属性投影到自定义对象上。

For V1, add the following filter to your profile:

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

Then you can do this:

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

BTW, if you are using the PowerShell Community Extensions you already have this.

Regarding the ability to use Select-Object -Expand in V2, it is a cute trick but not obvious and really isn't what Select-Object nor -Expand was meant for. -Expand is all about flattening like LINQ's SelectMany and Select-Object is about projection of multiple properties onto a custom object.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文