Get-ChildItem.Length 是错误的

Get-ChildItem.Length is Wrong

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

[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。有人知道如何解决这个问题吗?

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

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

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

使用 @(...) 数组子表达式运算符 ,可确保将包含的命令输出视为 array,即使只有1 object输出,这样保证.Length就是 ]数组.Length属性.

然而,在 PSv3+ 中,访问 .Count 而不是 .Length,如 WillPanic's helpful answer 也可以 - 见下文。

没有@(...),结果可能是单个 object,因为PowerShell 自动unwraps 一个仅包含 1 object 的输出 collection,它只产生 那个 object ,这意味着以下内容:

  • 最多 PSv2:

    • 如果 object 碰巧有 .Length 属性,则返回 值。
      在手头的例子中,如果返回的唯一 object 代表一个 file(一个 [System.IO.FileInfo] 实例)(如果目录包含只有 1 个文件,没有子目录,除了隐藏项)。
      [System.IO.FileInfo] 的实例的 .Length 属性 returns 文件的字节大小 。值 0 表示 空文件 .
      (如果返回的唯一 object 是一个 目录 (一个 [System.IO.DirectoryInfo] 实例,.Length 会返回 $null,因为这样的实例没有 .Length 属性.)
  • 在 PSv3+ 中,如果您使用 .Count,则不再严格需要解决方法,因为 您甚至可以治疗标量(单个object)就好像它一个数组,隐式.Length / .Count[1] 属性和索引的能力(例如,
    <scalar>[0])
    - 这些 隐式 成员被称为 intrinsic members - 但有 注意事项:

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

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

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

    • Member-access枚举是统一的补充方面,可以申请会员(属性 或方法)包含在 collection collection 级别 中的项目,在在这种情况下,成员在 collection 中的每个项目上被隐式 访问,结果值作为 数组 返回;请参阅下面的示例。
      要解决与 member-access 枚举的名称冲突,需要一种不同的方法 - 请参阅 this answer.


PSv3+ 统一 collection 处理示例

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-access 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 属性 是一个 别名 属性 of .Length,由 PowerShell 的 ETS 添加(扩展类型系统 - 参见 Get-Help about_Types.ps1xml)。 然而,自 PSv3 以来,这个别名 属性 并不是真正需要的,当时显式实现的 .NET 接口类型成员也被 PowerShell 公开,提供对数组类型 ICollection.Count 属性 的访问。因此,v6 将不再具有别名 属性,此时 .Count 将直接访问 ICollection.Count - 参见 GitHub issue #3222.
请注意,在 scalar 上调用 .Count 时,PowerShell 魔法(即 intrinsic 成员)仍然涉及(non-collection), 然而.