Почему Powershell молча преобразует строковый массив с одним элементом в строку

33

Рассмотрим следующий скрипт Powershell, который ищет папки в C: \ с именем 'og':

PS C: \> (ls |% {$ _. Name} |? {$ _. Contains ("og")})
Perflogs
Файлы программ
setup.log

Теперь я сужу поиск, чтобы получить только один элемент:

PS C: \> (ls |% {$ _. Name} |? {$ _. Contains ("Prog")})
Файлы программ

Странно то, что первая операция выдает массив , тогда как вторая операция (которая ИМХО является семантически той же самой операцией, поэтому она должна давать тот же тип результата) выдает строку . Это можно увидеть в следующем результате:

PS C: \> (ls |% {$ _. Name} |? {$ _. Contains ("og")}). Длина
3
PS C: \> (ls |% {$ _. Name} |? {$ _. Contains ("Prog")}). Длина
13

Это может быть очень раздражающим, так как очевидно, что меньше папок, которые соответствуют 'og', чем папок, которые соответствуют 'Prog'.

Очевидно, что PowerShell неявно «распаковывает» массив из одного элемента в один объект, и мы никогда не получаем массив длины 1. Кажется, что каждый раз, когда я хочу подсчитать результаты, поступающие по конвейеру, я должен проверить, не Я имею дело с массивом или нет.

Как я могу предотвратить это? Как вы справляетесь с этим?

cheesus ТАК перестать вредить Монике
источник
Они из StackOverflow могут помочь: stackoverflow.com/questions/1827862/… stackoverflow.com/questions/1390782/… Если вы не работали по трубопроводу $_.Contains, то %{,,$_.Name}работает ...
Боб,

Ответы:

56

Очевидно, что PowerShell неявно «распаковывает» массив из одного элемента в один объект,

И нулевой результат приводит к $null.

Как я могу предотвратить это?

Ты не можешь

Как вы справляетесь с этим?

Используйте конструктор массива ( @(...)) для принудительного возврата коллекции (возможно, с нулем или одним элементом):

$res = @(ls | %{$_.Name} | ?{$_.Contains("Prog")})
Ричард
источник
Спасибо, это прекрасно! Я буду голосовать, как только у меня будет 15 репутации.
cheesus ТАК перестать вредить Монике
2
Не уверен, что вы можете "заставить" это. @(1) | ConvertTo-Jsonвсе еще возвращается 1вместо [1].
Марк
@Marc: ConvertTo-Jsonникогда не возвращает коллекцию: он читает весь ввод и преобразует в одну строку. Если вы хотите, чтобы входные объекты конвертировались индивидуально, вам нужно обрабатывать каждый отдельно.
Ричард
1
@Richard, я думаю, вы неправильно поняли: я и многие другие, в основном, хотим, чтобы весь объект (то есть коллекция) был сериализован (например, для внешнего сохранения). Мы не заинтересованы в обработке каждого объекта в коллекции отдельно. ConvertTo-Json должен вернуть строку, которая, если ее выполнить, ConvertFrom-Json возвращает исходный объект, хотя и пустой массив / коллекцию.
Марк
@Marc Смысл этого вопроса состоит в том, чтобы избежать обработки массива из одного элемента в качестве этого элемента (что является меньшей проблемой из-за последующих изменений PSH: обратите внимание на дату вопроса). Вы говорите о совершенно другом случае (вынуждая коллекцию быть единым объектом), поэтому я недопонимаю.
Ричард
2

Это было решено в PowerShell v3:

http://blogs.microsoft.co.il/blogs/scriptfanatic/archive/2012/03/19/Counting-objects-in-PowerShell-3.0.aspx

В примечании вы можете найти, содержит ли имя что-то, используя подстановочный знак:

PS> ls *og*
Шей Леви
источник
6
Шей , я пока не могу комментировать ответы, но твое утверждение не соответствует действительности. PowerShell по-прежнему упаковывает элементы, но, как вы заметили, они дали отдельным элементам значение «Количество». Отдельные результаты все еще не распакованы. Вы можете проверить приведенный выше пример с PS 3 и посмотреть результаты.
Tohuw
1
Поведение все еще то же самое в PS 5.
MEMark
Да, def все еще присутствует
Джеймс Уайзман
1
Это поведение остается таким же в PS 6.0.1
spuder
2

Обратите внимание на разницу между этими двумя результатами:

PS C:\> ConvertTo-Json -InputObject @(1)
[
    1
]
PS C:\> @(1)|ConvertTo-Json
1
PS C:\>

Дело в том, что «распаковка» выполняется конвейером. ConvertTo-Json по-прежнему видит объект как массив, если мы используем InputObject, а не трубопровод.

Ларри Янг
источник