Get-ChildItem.Length неправильно
Я пишу рекурсивную функцию, которая проходит через каталог и копирует каждый файл и папку в нем. Первая проверка, которую я имею в функции, состоит в том, чтобы видеть, имеет ли пройденный путь дочерние элементы. Чтобы выяснить это, я использую следующий метод:
[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. Кто-нибудь знает, как обойти это?
2 ответа
Пытаться $pathChildren.Count
вместо $pathChildren.Length
- это вернет количество элементов в массиве.
PetSerAl, как и много раз ранее, предоставил критический указатель в кратком комментарии по этому вопросу (и он также помог в уточнении этого ответа):
$pathChildren = @(Get-ChildItem -Path $strPath)
Использование @(...)
оператор подвыражения массива гарантирует, что все выходные данные вложенной команды будут обрабатываться как массив, даже если выводится только 1 объект, так что .Length
гарантированно будет массив .Length
имущество.
Однако в PSv3+ доступ к .Count
вместо .Length
, как в полезном ответе WillPanic, работает тоже - см. ниже.
Без @(...)
результатом может быть один объект, поскольку PowerShell автоматически разворачивает выходную коллекцию, которая содержит только 1 объект, что дает только этот один объект, что подразумевает следующее:
до PSv2:
- Если этот один объект имеет
.Length
свойство, его значения возвращаются.
В данном случае это верно, если единственный возвращаемый объект представляет файл ([System.IO.FileInfo]
экземпляр) (что, в свою очередь, верно, если каталог содержит ровно 1 файл и не содержит подкаталогов, кроме скрытых элементов).
[System.IO.FileInfo]
инстанс.Length
свойство возвращает размер файла в байтах. Значение0
подразумевает пустой файл.
(Если единственным возвращенным объектом был каталог ([System.IO.DirectoryInfo]
пример,.Length
вернулся бы$null
потому что такие случаи не имеют.Length
имущество.)
- Если этот один объект имеет
в PSv3+ обходной путь больше не нужен строго, если вы используете
.Count
потому что вы можете обрабатывать даже скаляр (отдельный объект), как если бы это был массив, с неявным.Length
/.Count
[1] свойства и способность индексировать в (например,
<scalar>[0]
), но есть предостережения:Если
Set-StrictMode -Version 2
или выше, доступ к.Length
а также.Count
свойства, которые на самом деле не существуют в скаляре под рукой, вызывают ошибку.
Однако такое поведение весьма прискорбно, поскольку считается, что эти свойства существуют неявно - если вы согласны, выскажите свой голос в этой проблеме GitHub.Если скаляр сам по себе имеет такое свойство, как
.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
собственность была псевдонимом собственности .Length
, добавленный PowerShell ETS (система расширенного типа - см. Get-Help about_Types.ps1xml
, Тем не менее, это свойство псевдонима на самом деле не требовалось с PSv3, когда явным образом реализованные члены типа интерфейса.NET были также представлены PowerShell, предоставляя доступ к типу массива. ICollection.Count
имущество. Поэтому v6 больше не будет иметь псевдонима, после чего .Count
будет иметь прямой доступ ICollection.Count
- увидеть этот вопрос GitHub.
Обратите внимание, что магия PowerShell все еще задействована, когда дело доходит до вызова .Count
на "поддельном" массиве (скаляр), однако.