在 PowerShell 中筛选特定扩展名(例如.md)文件的“最佳实践”是什么?

2021-05-01 10:00:00 +08:00
 AndyAO

本来以为 Selet-Object 应该能发挥作用,但看过帮助文档之后发现对于筛选来说它只能执行比较简单的操作(First, Last, Unique, Skip, and Index)。

目前我的方法是用%,获取文件列表之后,根据后缀属性用if判断,然后再直接返回。

        $FileList = $Dir | Get-ChildItem -Recurse -File
        $PyFileList = $FileList | ForEach-Object { if ($_.Extension -eq '.py') { $_ } }

这种办法目前用起来也还可以,但感觉用if(){},总是不 PowerShellic 。

那么针对这种需求的最佳实践是什么?

高度怀疑应该是有更好的方式的,如果没有的话自己写个 Cmdlet,倒是不错,直接传递脚本块儿就根据布尔值返回对象,从而免去各种括号。这个需求是高频的。

1595 次点击
所在节点    问与答
16 条回复
Yadomin
2021-05-01 10:08:30 +08:00
难道不是 Get-Item *.md 吗
Yadomin
2021-05-01 10:09:05 +08:00
Get-ChildItem (
chinvo
2021-05-01 10:10:01 +08:00
-Filter
AndyAO
2021-05-01 10:12:08 +08:00
@Yadomin #1 自信点,的确是(笑哭
不过再复杂点的属性怎么办呢?
h404bi
2021-05-01 10:16:42 +08:00
写得真复杂...

Get-ChildItem -Include "*.md" -Recurse

@AndyAO #4 不明白再复杂点的属性是什么样的需求
AndyAO
2021-05-01 10:19:24 +08:00
找到了,应该用 Where-Object,这也是个很常用的 Cmdlet,别名是?
AndyAO
2021-05-01 10:22:11 +08:00
⚠⚠⚠
 
人们经常会将 Select-Object 和 Where-Object 这两个 PowerShell 命令搞混,虽然目前你还没有见过 Where-Object 。 Select-Object 用于选择所需的属性(或列),还可以选择输出行的任意子集(使用 -First 和 -Last )。 Where-object 基于筛选条件从管道中移除或过滤对象。
AndyAO
2021-05-01 10:31:03 +08:00
@h404bi 例如,根据文件长度的范围。
AndyAO
2021-05-01 10:32:24 +08:00
@h404bi #5 又例如,根据文件内容是否包含某字符串,目前感觉 Where-Object 是最佳实践
```PowerShell
$PyFileList | Where-Object {($_ | Get-Content -Raw).Contains('Unit.')}
```
AndyAO
2021-05-01 10:37:26 +08:00
h404bi
2021-05-01 10:42:48 +08:00
@AndyAO #8 组合条件过滤确实得用上 Where-Object 。不过就标题的问题来说,用通配符是最 PowerShell 的写法,官方文档给的 examples 。

如果用过 Where-Object 的 alias ( ? ),应该就很容易记住管道过滤时用它了。:D
zgzb
2021-05-01 11:52:40 +08:00
foreach($Dir in dir *.md,*.txt,*.html)
geelaw
2021-05-01 12:21:06 +08:00
Select-Object 主要功能是 map,比如从具有 A B C 三个属性的对象中获得具有 A B 两个属性的对象,而不是根据属性留下或去掉对象。

注意用 Where-Object 或者 ForEach-Object 或者 ForEach 循环筛选文件和直接用通配符是不同的,取决于具体写法,因为 string 对象的方法默认是大小写敏感、当前文化,而 Get-ChildItem 的 globbing 是大小写不敏感(我不清楚它比较的文化是什么)。

例如 Get-ChildItem | Where-Object { $_.EndsWith('.ps1') } 无法得到扩展名是 .Ps1 或者 .PS1 或者 .pS1 的文件。

另外“扩展名”的定义也比较模糊,我想你的意思应该是指“长扩展名”,即不考虑短文件名的情况(例如 a.theme 的短文件名是 a.the ,因此 .the 是 a.theme 的短扩展名)。好消息是:PowerShell 默认情况下不会用通配符匹配短文件名。
AndyAO
2021-05-01 12:24:26 +08:00
@geelaw #13
你的回复考虑的真周全!
对于比较比较复杂的情况,会经常使用的代码,这些情况的确是要考虑的。
imn1
2021-05-01 12:58:34 +08:00
powershell 如果想快、高效,尽可能用.net 框架下的命令,ps 原生的命令当然比较 PowerShellic,但做大量时工作有点慢
AndyAO
2021-05-01 13:27:50 +08:00
1. 在 PowerShell 中 glob 和通配符应该是同义词[^1],-like 的行为就是所有通配符参数的标准行为
2. -eq 对于字符串的比较也是忽略大小写的,这个和 C#上的默认行为是完全不同的

[^1]:Using wildcard characters is sometimes referred to as globbing. from: https://docs.microsoft.com/en-us/powershell/scripting/developer/cmdlet/supporting-wildcard-characters-in-cmdlet-parameters?view=powershell-7.1

@imn1 #15

的确是这样的,本来 PowerShell 的实现好像就不怎么高效吧,而且为了交互式运行方便,会添加很多的额外属性,而且会默认产生很多的展示用信息,所以速度慢很多很多。

对于很轻的任务来说,编写效率要远远重于运行效率,PowerShellic 是很值得的;如果有执行大量运算的任务,确实应该用.Net Native 来加速,这就有点类似于 Ruby 和 Python 之于 C 。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/774469

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX