Как свойство Count работает в Powershell?
Я сделал самую неудачную опечатку, которая стоила мне довольно дорогое время:
$errors.Count
Это возвращает "0
", даже если есть ошибки, потому что имя переменной должно быть сингулярным. Это работает:
$error.clear() # To ensure a correct repro
Copy-Item asdf fdsa # Will show an error
$error.Count # Will output "1"
Однако теперь я хочу знать, почему $errors.Count
дал мне что-нибудь вообще, и почему он дал мне "0
". Поэтому я продолжил некоторые тесты и получил следующие результаты:
$asdf.Count # Will output "0"
$nrOfEinsteinsInSpace.Count # Will output "0"
$a = 0; $a.Count; # Will output "1"
$b = 2; $a.Count; # Will output "1"
$x = 1,2,3; $x.Count; # Will output "3"
И собрав еще больше данных, чтобы иметь возможность задать мне разумный вопрос:
$true.Count # Will output "1"
$false.Count # Will output "1"
Итак, мы имеем следующие различные случаи:
- Array (например) переменные, где
.Count
выводит количество элементов.
- несуществующие переменные, где
.Count
выводит "0".
- Объявленные переменные, где
.Count
будет выводить "1".
- Встроенные переменные, где
.Count
будет выводить "1".
Случаи 2, 3 и 4 не имеют для меня никакого смысла (пока). Что здесь происходит? Где это документировано? Как работает свойство .Count
?
Ответы
Ответ 1
Начиная с PowerShell V3, свойства Count и Length получили очень специальную обработку, не связанную с данными расширенного типа (также известными как ETS или система расширенного типа).
Если экземпляр имеет свойство Count/Length, то все продолжает работать так же, как в V1/V2 - возвращается значение из экземпляра.
Если экземпляр не имеет свойства Count/Length, начиная с V3, вместо того, чтобы быть ошибкой, вы вернетесь 1. И если экземпляр равен $null, вы вернетесь в 0. Если вы включили строгий режим, вы получите ошибку, как в V2.
Я признаю, что это немного странно, но он решает общую проблему, когда командлет возвращает 0, 1 или несколько объектов.
Часто вы будете проходить через эти результаты по конвейеру или с помощью инструкции foreach. Например:
dir nosuchfile* | % { $_ }
foreach ($item in dir nosuchfile*) { $_ }
В случае foreach выше, V2 фактически войдет в цикл, если команда не вернет никаких значений. Это было изменено в V3, чтобы лучше соответствовать ожиданиям людей, но это также означало, что:
foreach ($item in $null) { $_ }
также никогда не входит в цикл.
Оператор for - это еще один способ выполнить итерацию результатов, например
$results = dir nosuchfile*
for ($i = 0; $i -lt $results.Count; $i++) { $results[$i] }
В этом примере неважно, сколько объектов в $result, цикл работает отлично. Обратите внимание, что в V1/V2 вы бы написали:
$results = @(dir nosuchfile*)
Это гарантирует, что $results является массивом, но этот синтаксис является уродливым, и многие люди его забудут, следовательно, изменение поддержки Count/Length немного более прощающим способом.
Ответ 2
Чтобы дополнить ответ Павла, это может быть связано с данными расширенного типа. Чтобы процитировать соответствующую часть документации:
Данные расширенного типа определяют дополнительные свойства и методы ( "члены" ) типов объектов в Windows PowerShell. Вы можете расширить любой тип, который поддерживается Windows PowerShell и использует добавленный свойства и методы так же, как вы используете свойства которые определены для типов объектов.
и
В Windows PowerShell есть три источника данных расширенного типа сессий. Файлы Types.ps1xml в каталоге установки Windows PowerShell загружаются автоматически в каждый сеанс Windows PowerShell.
Если вы откроете файл Types.ps1xml (в $pshome
), вы увидите это в начале файла:
<Type>
<Name>System.Array</Name>
<Members>
<AliasProperty>
<Name>Count</Name>
<ReferencedMemberName>Length</ReferencedMemberName>
</AliasProperty>
</Members>
</Type>
Итак, я предполагаю, что, предоставляя свойство ".Count
", PowerShell предполагает, что это массив.
Ответ 3
Вот как я думаю, что это работает:
Случай 1: в массиве свойство .Count
действительно ссылается на свойство .Length
, которое показывает количество элементов в массиве
Случай 2: переменные без вывода автоматически создаются с помощью powershell и инициализируются значением $null
Случай 3/4: В этом я не совсем уверен, почему это происходит, но поскольку ни String, ни Int, ни логические объекты не имеют свойства .Count
, я могу представить, что свойство наследуется родительским объектом.
Поведение указывает на то, что переменная рассматривается как массив, поэтому с 1 значением, назначенным для вывода, будет 1, без значения результат будет равен 0.
Изменить:
Для полноты здесь ссылка на документацию: Technet, спасибо @David Brabant