使用Get-ChildItem -Exclude或-Include返回空

15

我在C:目录中有一个包含多个文件的列表。如果我尝试在它上面运行-Exclude,我得不到任何返回结果。对于-Include也是一样。如果我使用-Filter,就能得到我期望得到的结果。我是否没有理解它应该做什么?

以下是我运行并得不到任何东西的示例:

Get-ChildItem -Path C: -Exclude "*.txt"

我没有任何返回。 如果我运行

Get-Childitem -filter "*.txt"

我得到了这个返回值:

  Mode                LastWriteTime         Length Name                                                                               
----                -------------         ------ ----                                                                               
-a----        11/7/2007   8:00 AM          17734 eula.1028.txt                                                                      
-a----        11/7/2007   8:00 AM          17734 eula.1031.txt                                                                      
-a----        11/7/2007   8:00 AM          10134 eula.1033.txt                                                                      
-a----        11/7/2007   8:00 AM          17734 eula.1036.txt                                                                      
-a----        11/7/2007   8:00 AM          17734 eula.1040.txt                                                                      
-a----        11/7/2007   8:00 AM            118 eula.1041.txt                                                                      
-a----        11/7/2007   8:00 AM          17734 eula.1042.txt                                                                      
-a----        11/7/2007   8:00 AM          17734 eula.2052.txt                                                                      
-a----        11/7/2007   8:00 AM          17734 eula.3082.txt                                                                      
               7/7/2016   8:50 AM             93 HaxLogs.txt                                                                        
-a----         7/8/2016   8:30 AM              0 Test.txt 

对我来说,它可以正常工作,但你是否尝试过不使用引号来指定扩展名呢?例如 Get-ChildItem -Path C: -Exclude *.txt ? - nyagolova
4个回答

43
总结并补充gravity'sBrian Reynolds's的有益回答:
你的方法存在两个明显问题:
  • 针对 C: 可能不会总是达到你想要的效果,因为 C: 指的是当前位置(工作目录)在 C: 驱动器上的任何内容。

    • 要定位到 C: 驱动器的根文件夹,你必须使用 C:\,我假设这是你在本答案的其余部分中所指的。
  • 在没有 -Recurse 参数或者 -Path 值为 * 或以 \* 结尾的情况下,使用 -Exclude(以及 -Include)参数通常不会产生任何结果。出乎意料吗?确实如此 - 请参阅下面的更多信息。

    • 因此,Get-Item -Path C:\* -Exclude *.txt - 注意从 Get-ChildItem 切换到 Get-Item 并在 C:\ 后面加上 * - 这样才能使你的命令仅适用于位于 C:\ 直接下的项目。

背景信息:

通常情况下,使用提供程序本机的-Filter参数比使用-Include更可取,因为:

  • 由于提供程序本身在源头处执行过滤操作,所以-Filter-Include要快得多。而-Include会在所有对象接收完之后,由PowerShell应用过滤器。

  • 使用-Filter不需要切换到Get-Item并在-Path参数值后添加\*

    • 例如,Get-ChildItem -Path C:\ -Filter *.txt可以很好地匹配C:根目录中的所有*.txt文件。

话虽如此,还是有一些注意事项

  • -Filter 支持的通配符模式语言比 PowerShell 的功能更少,特别是它不支持字符集/范围,如 [0-9],可能意外匹配短(8.3)文件名,并具有其他遗留怪异之处 - 请参阅这个经过深入研究的答案以了解详细信息。

  • -Filter 仅支持一个模式,而 -Include 支持多个模式(模式数组)。

不幸的是,-Filter 总是一个正向(包含型)过滤器,因此无法用来提供 -Exclude 的功能。


使用-Include / -ExcludeGet-ChildItem的实现方式不直观且存在陷阱:

顺便提一下:如果你只使用一个-Include模式(而不是-Exclude),可以直接将该模式附加到-Path参数上,这样更容易操作;例如:Get-ChildItem C:\*.txt

简而言之

如果你在使用-Recurse时希望获得可预测的-Include / -Exclude行为(如果你没有使用-Recurse,则不需要此解决方法):

# IMPORTANT: Workaround isn't needed if you're using -Recurse.
#   * "\*" was appended to the input path
#   * Get-*ChildItem* was switched to Get-*Item*
Get-Item C:\path\to\* -Include ...
Get-Item C:\path\to\* -Exclude ...

在 PowerShell (Core) v7+ 中,如果你的输入路径是字面意义上的,你可以选择使用 `Get-ChildItem` 和 `-LiteralPath` 而不是(可能位置隐含的)`-Path`(使用 `.` 表示当前目录)。
# IMPORTANT: Works in PowerShell (Core) only.
#            Note the use of -LiteralPath.
Get-ChildItem -LiteralPath C:\path\to -Include ...
Get-ChildItem -Literalpath C:\path\to -Exclude ...

  • Windows PowerShell中存在一个明显的错误,当使用-LiteralPath时,-Include / -Exclude会被静默忽略。这个问题在PowerShell (Core)中已经修复,至少从v7.0开始(可以合理地假设它不会在Windows PowerShell中修复,后者只会接收到关键修复)。

  • -Include-Exclude的工作方式与直觉相反,这是GitHub问题#3304的主题:

    • -Include-Exclude修改-Path参数的最后路径组件,即文件系统路径的文件或目录名称

    • 这意味着模式首先应用于指定的文件夹路径本身的叶子组件,然后再应用于子项(如果有的话)。

    • 如果输入路径不以\*结尾且未指定-Recurse,则其影响如下:

      • -Include:如果输入路径的最后一个路径组件与-Include模式不匹配,则输入路径本身将被排除在外(不包括),并且路径的子项将永远不会被查看 - 没有输出。

      • -Exclude:类似地,如果输入路径的最后一个路径组件与-Exclude模式匹配,则输入路径本身将被排除在外,并且路径的子项将永远不会被查看 - 没有输出。

        • 针对根目录的操作 - 例如,Get-ChildItem -Path C:\ -Exclude Windows)似乎在v7.0中完全失效:它要么根本不产生输出,要么在类Unix平台上失败,无论使用的是-Include还是-Exclude,无论使用的是哪种模式 - 参见GitHub问题#11649
      • 如前所述,如果使用-Recurse,问题就不会出现,因为这会强制进入输入路径的子树,即使输入路径本身未包含/排除。

  • 除非需要-Recurse,否则获得预期行为的唯一方法是将
    Get-ChildItem C:\path\to -Include / -Exclude替换为
    Get-Item C:\path\to\* -Include / -Exclude - 注意使用Get-Item而不是Get-ChildItem,并且在-Path参数后附加了\*

    • 相比之下,如果您使用Get-ChildItem *-Exclude结合使用,并且在非排除项中存在目录Get-ChildItem将意外地输出它们的内容;这在使用通配符

      示例:使用-Include / -Exclude的问题

      注意:为了使下面的所有命令按照直觉的预期工作,请将Get-ChildItem C:\Windows替换为Get-Item C:\Windows\* - 注意使用了不同的cmdletGet-Item和附加的\*

      # HAPPENS TO WORK, BUT IS NOT ROBUST:
      # Matches all w* items *inside* C:\Windows, but
      # ONLY because w* happens to match 'Windows' - the last input
      # path component - too.
      Get-ChildItem C:\Windows -Include w*
      
      # HAPPENS TO WORK, BUT IS NOT ROBUST:
      # Matches all items whose names *don't* start with a-v *inside* C:\Windows, but
      # ONLY because [a-v]* happens not to exclude 'Windows' - the last input
      # path component - too.
      Get-ChildItem C:\Windows -Exclude [a-v]*
      
      
      # OUTPUTS NOTHING:
      # Because t* doesn't match 'Windows', the child items of 
      # 'C:\Windows' are not considered.
      Get-ChildItem C:\Windows -Include t*
      
      # OUTPUTS NOTHING:
      # Because w* matches 'Windows', it is excluded, and
      # the child items of 'C:\Windows' are not considered.     
      Get-ChildItem C:\Windows -Exclude w*
      

4
哇塞,这个彻底融合了所有各种挂念之处!你真的深入探究了其原因!:) 你是在哪里找到这些资料的,还是自己研究的? - gravity
3
这太糟糕了,几乎无法置信。它字面上说-Include/-Exclude已知只会被悄悄忽略;因此它们是不可靠的。编写带有-Exclude选项的脚本删除子目录将是极具危险性的,因为它可能会删除它们。这是一个巨大的问题,因为Remove-Item文档表示其-Recurse参数也“已知无法正常工作”,并建议使用Get-ChildItem将其输出到命令作为一种解决方法。但是Get-ChildItem显然也存在问题!真是一团糟。 - Triynko
P.S.,@Triynko:GitHub问题#5699中讨论了一个类似的可能具有破坏性的问题。 - mklement0
小修正,@Triynko:-Include / -Exclude 不是被_忽略_ - 它们只是没有按照人们期望的方式应用:它们不适当地应用于_输入_路径(首先)而不是(仅)应用于_它们的子项_,并且由此产生的看似不可预测的行为可能比仅仅忽略还要糟糕。至于 Remove-Item 帮助主题中关于 -Recurse / 将 -Recurse-Include 结合使用的警告:让我们尝试澄清/更新/删除这些内容:请参见 Github 文档问题 #8134 - mklement0
我面临了一个独特的问题,需要获取所有文件和文件夹名称中没有点的内容。显然,"-Filter *." 是解决方案,尽管该参数应该是包含性的。真正的黑魔法。 - AgentRev
1
@AgentRev,是的,这很反直觉,但这并不是PowerShell的错:-Filter参数通常是特定于提供程序的,对于文件系统路径来说,这意味着底层的WinAPI处理通配符匹配,它有很多遗留问题,比如你提到的那个 - 参见这个答案 - mklement0

11
Get-ChildItem -Path "C:\*" -Include "*.txt"

这个例子展示了-Include的工作原理,它将给你期望的结果。请注意,我在路径参数中提供了通配符,明确将路径定义为“根C:盘中的任意文件”,而不是仅限于“C:”本身。

来源: https://technet.microsoft.com/library/hh849800.aspx

如果链接失效,请参考该链接中的示例3(请注意路径中的通配符):

C:\> Get-ChildItem –Path "C:\Windows\Logs\*" -Include "*.txt" -Exclude "A*"

1
谢谢。由于某种原因,路径中的通配符无法正常工作。 - bradrice
4
这种事情让我发疯:当 API / 命令根据输入以不同的方式运行时。当然,这应该发生,但我特指的是这种情况。当你仅传递路径时,它会假设通配符 *。当你添加 -Include-Exclude 时,它停止做出这个假设了,而文档并没有说明这一点。更重要的是,命令被称为 Get-ChildItem。它应该获取子项。在所有情况下,通配符都应该是不必要的。 - Chris76786777
3
哇,显然在路径中添加通配符让一切都正常工作了。那真是太令人困惑了。 - Kellen Stuart

2

实际上并不是这样的。你可以自己试试看。Get-ChildItem -Path "C:\" -Include "*.txt" 没有返回任何结果,而 Get-ChildItem -Path "C:\*" -Include "*.txt" 则会显示所有扩展名为 .txt 的文件。 - gravity
是的,除非我在C:\后面添加*。否则我得不到任何结果。我不确定为什么,因为我在C:位置上,如果我使用Get-ChildItem C:或Get-ChildItem C:\,我会得到所有文件。似乎只有-Include和-Exclude对我有用。我正在运行PSVersion-5.0.10586.122。 - bradrice
很不幸,如果没有使用-Recurse,那么在-Include / -Exclude中添加*是必需的。但是这个答案提到了关于C:C:\的重要观点。 - mklement0

0

这似乎是一个 bug(因为父属性的字符串版本为空字符串?)。在子目录中,它可以正常工作:

get-childitem c:\windows -directory -exclude *.dll

您可以以不同的方式指定根目录,并获得奇怪的错误消息:

get-childitem Microsoft.PowerShell.Core\FileSystem::C:\ -exclude *.txt

get-childitem : Cannot process argument because the value of argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ get-childitem Microsoft.PowerShell.Core\FileSystem::C:\ -exclude wind ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ChildItem], PSArgumentException
    + FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.GetChildItemCommand

或者,在OSX中:

get-childitem / -directory -exclude *.txt

get-childitem : Cannot process argument because the value of argument "path" is not valid. Change the value of the "path" argument and run the operation again.
At line:1 char:1
+ get-childitem / -directory -exclude var
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidArgument: (:) [Get-ChildItem], PSArgumentException
+ FullyQualifiedErrorId : Argument,Microsoft.PowerShell.Commands.GetChildItemCommand

这是针对 PowerShell 6 及以上版本的解决方法:

get-item c:\ | get-childitem -exclude *.txt

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