PowerShell的Get-ChildItem -Filter和Where语句在取相同值时有不同的操作方式。

21

我在服务器上有一个名为MyFolder的文件夹。此外,还有名为MyFolder.1、MyFolder.2、MyFolder.3等的其他文件夹。

如果我运行:

gci C:\Sample | ? { $_.Name -like "MyFolder.*" }

我得到了预期的输出:

    Directory: C:\Sample


Mode                LastWriteTime     Length Name                                                                      
----                -------------     ------ ----                                                                      
d----        16/10/2012     12:16            MyFolder.1                                                                
d----        16/10/2012     12:16            MyFolder.2                                                                
d----        16/10/2012     12:16            MyFolder.3  

然而,如果我运行:

gci C:\Sample -Filter "MyFolder.*"

我得到:

    Directory: C:\Sample


Mode                LastWriteTime     Length Name                                                                      
----                -------------     ------ ----                                                                      
d----        16/10/2012     12:16            MyFolder                                                                  
d----        16/10/2012     12:16            MyFolder.1                                                                
d----        16/10/2012     12:16            MyFolder.2                                                                
d----        16/10/2012     12:16            MyFolder.3                                                                

我对于MyFolder是如何被包含在输出中感到困惑。我期望输出结果不会变化。

在线帮助指出筛选器语法基于提供程序,但我不确定在此实例中使用了哪个提供程序。

我是否缺少一些基础知识?我尝试将正则表达式字符串传递到筛选器中,例如 "MyFolder\.*",但这只返回空值。我确信我错过了一些简单的东西。

我正在运行PowerShell版本2。

解决方法

感谢Roman Kuzmin指出通配符匹配的差异。以下内容可获得预期输出:

gci C:\Sample\MyFolder.*
我将来会使用这种语法,以便在代码中减少噪音。

4
如果微软更新帮助文档,解释文件系统提供程序中“-Filter”功能的作用会很好,因为这可能是99.9%和一半人在使用“Get-ChildItem”命令时最常见的情况。 “语法基于提供程序”似乎有点不真诚。 - Todd Walton
3个回答

23

FileSystem提供程序的Filter使用CMD通配符而不是PowerShell通配符。在某些边缘情况下,CMD通配符很有趣,但不直观,这主要是历史原因。这里有一个有趣的解释:https://devblogs.microsoft.com/oldnewthing/20071217-00/?p=24143

还有一点需要注意的问题:实际上,ls -Filter *.txt获取的是像PowerShell意义上的*.txt*那样的扩展名以txt开头的文件。这可能在某些场景中是意外和非常不愉快的 :)


感谢您提供链接并指出通配符的差异。这有助于我解决问题(但不是我预期的方式...) - Kieranties
关于-Filter *.txt的优秀观点是它的行为类似于-Filter *.txt*。但有一个小问题:所链接的文章谈论的是MS-DOS,而不是CMD(cmd.exe)。该文章接着说(强调添加),“但是FCB匹配算法的一些怪癖因为已经成为习惯而在Win32中仍然存在。”换句话说,Windows API提供了当前的行为,详见此答案 - mklement0
3
我可以证明使用-Filter并对数据库运行一组SQL脚本时,如果文件夹中有一个名为filename.sql-skip的脚本,会产生“意外和不愉快”的效果。 - TTT

12
gci C:\Sample -Filter "MyFolder.*"  # here is a filesystem provider; use wildcard `*`,`?`

返回与(cmd.exe shell中)相同的输出:

dir Myfolder.* 
如果你需要使用正则表达式,可以这样做(-filter 不支持正则表达式)。
gci C:\Sample | ? { $_.Name -match '^MyFolder\..*' }

就像这样

gci C:\Sample | ? { $_.Name -like "MyFolder.*" }

脚本块中的比较是针对 [string] 类型进行的。


谢谢Christian,我在重新阅读在线文档后意识到Filter不会接受正则表达式。 - Kieranties

0
这里有关于筛选行为的一些信息 https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/get-childitem?view=powershell-7.3 MS示例。

当使用-Include参数时,如果在路径中不包含星号

命令将不返回任何输出。

Get-ChildItem -Path C:\Test\ -Include *.txt

Get-ChildItem -Path C:\Test* -Include *.txt

       Directory: C:\Test

       Mode                LastWriteTime         Length Name
      ----                -------------         ------ ----
      -a----        2/13/2019     08:55             26 anotherfile.txt
      -a----        2/12/2019     15:40         118014 Command.txt
      -ar---        2/12/2019     14:31             27 ReadOnlyFile.txt

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