2

我正在编写一个遍历目录并复制其中的每个文件和文件夹的递归函数。我在函数中的第一个检查是查看传入的路径是否有子路径。为了找出这一点,我使用以下方法:

[array]$arrExclude = @("Extras")
Function USBCopy
{
Param ([string]$strPath, [string]$strDestinationPath)
    try
    {
        $pathChildren = Get-ChildItem -Path $strPath
        if($pathChildren.Length -gt 0)
        {
            foreach($child in $pathChildren)
            {
                if($arrExclude -notcontains $child)
                {
                    $strPathChild = "$strPath\$child"
                    $strDestinationPathChild = "$strDestinationPath\$child" 
                    Copy-Item $strPathChild -Destination $strDestinationPathChild
                    USBCopy $strPathChild $strDestinationPathChild  
                }   
            }
        }              
    }
    catch
    {
        Write-Error ("Error running USBCopy: " + $Error[0].Exception.Message)
    }    
}

在大多数情况下,我的函数可以工作,但我的代码会说一个目录是空的,而它实际上有 1 个文件。当我调试我的函数时,变量会说文件夹有子文件夹,但变量的长度为 0。有人知道如何解决这个问题吗?

4

2 回答 2

6

PetSerAl和以前一样,在对该问题的简短评论中提供了关键指针(他还协助完善了这个答案):

$pathChildren = @(Get-ChildItem -Path $strPath)

数组子表达式运算符@(...)使用确保任何封闭的命令输出都被视为一个数组,即使只有1 个对象被输出,所以它.Length保证是数组.Length属性。

但是,在 PSv3+ 中,访问.Count而不是.Length,如WillPanic 的有用答案也可以- 见下文。

如果没有@(...),则结果可能是单个对象,因为 PowerShell会自动解包一个仅包含1 个对象的输出集合,从而生成该对象 only,这意味着以下内容:

  • 高达PSv2

    • 如果该对象碰巧具有.Length属性,则返回值。
      在目前的情况下,如果返回的唯一对象代表一个文件(一个[System.IO.FileInfo]实例),则为真(如果目录恰好包含 1 个文件且没有子目录,隐藏项除外,则反过来为真)。
      A[System.IO.FileInfo]的实例的.Length属性以字节为单位返回文件的大小。的值0意味着一个空文件
      (如果返回的唯一对象是一个目录(一个[System.IO.DirectoryInfo]实例,.Length将会返回$null,因为这样的实例没有.Length属性。)
  • 在 PSv3+ 中,如果您使用 ,则不再严格需要解决方法.Count,因为您甚至可以将标量单个对象)视为一个数组,具有隐式 .Length/ .Count[1] 属性和索引到(例如,
    <scalar>[0]
    的能力- 这些隐式成员被称为内在成员- 但有一些警告

    • 如果或更高有效,则对手头上实际不存在的标量的Set-StrictMode -Version 2访问.Length和属性会导致错误。 然而,这种行为非常不幸,因为这些属性应该被认为是隐式存在的——如果您同意,请在GitHub 问题 #2798中表达您的意见。.Count

    • 如果标量本身具有诸如.Length.Count或支持索引之类的属性,则优先- 这就是为什么.Count必须在这种情况下使用(如前所述,[System.IO.FileInfo]实例具有.Length以字节为单位报告文件大小的属性);有关示例,请参见下文。

    • 使用@(...)避免了这样的冲突,因为结果总是一个数组。

    • 成员枚举是统一的补充方面,它允许您在集合级别应用集合中包含的项的成员(属性或方法) ,在这种情况下,成员在集合中的每个项上都被隐式访问,并且结果值作为数组返回;请参阅下面的示例。 要解决与成员枚举的名称冲突,需要一种不同的方法 - 请参阅此答案


PSv3+统一集合处理示例

PS> (666).Length
1  # Scalar 666 was implicitly treated as a collection of length 1

PS> (666).Count
1  # Ditto - ** .Count is preferable, because it less often means something else **

# Caveat: A *string* scalar has a native .Length property
PS> ('666').Length; ('666').Count
3  # .Length: The string types's native property: the number of *characters*
1  # .Count: PowerShell's implicit collection handling: 1 *element*

PS> (666)[0]; (666)[-1]
666  # Index [0] always yields the scalar itself.
666  # Ditto for [-1], the *last* element.

# Member enumeration example: get the .Day property value from each
# [datetime] instance stored in an array.
PS> ((Get-Date), (Get-Date).AddDays(-1)).Day
20
19

[1] 正如 PetSerAl 指出的,直到 PSv5.1,数组的.Count属性是PowerShell 的 ETS(扩展类型系统 - 请参阅)添加的别名属性。然而,自从 PSv3 以来,这个别名属性就不再需要了,当显式实现的 .NET 接口类型成员也被 PowerShell 公开时,提供了对数组类型属性的访问。因此,v6 将不再具有 alias 属性,此时将直接访问- 请参阅GitHub 问题 #3222。但是,请注意,在调用标量(非集合)时,仍然涉及 PowerShell 魔术(即内在成员)。.LengthGet-Help about_Types.ps1xmlICollection.Count.CountICollection.Count
.Count

于 2017-05-17T22:36:32.090 回答
4

尝试$pathChildren.Count而不是$pathChildren.Length- 这将返回数组中的项目数。

于 2017-05-17T22:00:14.533 回答