问题
I am writing a recursive function that goes through a directory and copies every file and folder in it. The first check I have in the function is to see if the path passed in has children. To find this out, I use the following method:
[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)
}
}
For the most part my function works, but my code will say a directory is empty when it actually has 1 file in it. When I debug my function, the variable will say that the folder has children but the variable's length is 0. Anyone know how to get around this?
回答1:
Try $pathChildren.Count instead of $pathChildren.Length - that will return the number of items in the array.
回答2:
PetSerAl, as many times before, has provided the crucial pointer in a terse comment on the question (and he's also assisted in refining this answer):
$pathChildren = @(Get-ChildItem -Path $strPath)
The use of @(...), the array subexpression operator, ensures that whatever the enclosed command outputs is treated as an array, even if only 1 object is output, so that .Length is guaranteed to be the array's .Length property.
However, in PSv3+, accessing .Count instead of .Length, as in WillPanic's helpful answer, works too - see below.
Without @(...), the result may be a single object, because PowerShell automatically unwraps an output collection that contains only 1 object, which yields that one object only, which implies the following:
up to PSv2:
- If that one object happens to have a
.Lengthproperty, its values is returned.
In the case at hand, this is true if the only object returned represents a file (a[System.IO.FileInfo]instance) (which in turn is true if the directory contains exactly 1 file and no subdirectories, hidden items aside).
A[System.IO.FileInfo]'s instance's.Lengthproperty returns the file's size in bytes. A value of0implies an empty file.
(If the only object returned had been a directory (a[System.IO.DirectoryInfo]instance,.Lengthwould have returned$null, because such instances don't have a.Lengthproperty.)
- If that one object happens to have a
in PSv3+, the workaround is no longer strictly needed, if you use
.Count, because you can treat even a scalar (single object) as if it were an array, with implicit.Length/.Count[1] properties and the ability to index into (e.g.,<scalar>[0]), but there are caveats:If
Set-StrictMode -Version 2or higher is in effect, access to.Lengthand.Countproperties that don't actually exist on a scalar at hand cause an error.
This behavior is quite unfortunate, however, as these properties should be considered to exist implicitly - if you agree, make your voice heard in this GitHub issue.If the scalar itself has a property such as
.Lengthor.Countor supports indexing, that takes precedence - this is why.Countmust be used in this case (as stated,[System.IO.FileInfo]instances have a.Lengthproperty reporting the file size in bytes); see below for examples.Using
@(...)avoids such collisions, because the result is always an array.Member enumeration is the complementary aspect of unification, which allows you to apply a member (property or method) of the items contained in the collection at the collection level, in which case the member is implicitly accessed on every item in the collection, and the resulting values are returned as an array; see below for an example.
To resolve name collisions with member enumeration, a different approach is needed - see this answer of mine.
Examples of PSv3+ unified collection handling
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] As PetSerAl points out, up to PSv5.1, an array's .Count property was an alias property of .Length, added by PowerShell's ETS (extended type system - see Get-Help about_Types.ps1xml).
However, this alias property hasn't really been needed since PSv3, when explicitly implemented .NET interface type members were exposed by PowerShell too, providing access to the array type's ICollection.Count property. v6 will therefore no longer have the alias property, at which point .Count will directly access ICollection.Count - see this GitHub issue.
Note that PowerShell magic is still involved when it comes to invoking .Count on a "fake" array (a scalar), however.
来源:https://stackoverflow.com/questions/44035319/get-childitem-length-is-wrong