Чому Powershell мовчки перетворює масив рядків з одного елемента в рядок


33

Розглянемо наступний скрипт Powershell, який здійснює пошук папок у C: \ з позначкою 'og':

PS C: \> (ls |% {$ _. Ім'я} |? {$ _. Містить ("og")})
PerfLogs
Програмні файли
setup.log

Тепер я звужую пошук, щоб отримати лише один предмет:

PS C: \> (ls |% {$ _. Ім'я} |? {$ _. Містить ("Прог")})
Програмні файли

Дивна річ у тому, що перша операція дає масив , тоді як друга операція (яка є ІМХО семантично однаковою операцією, тому вона повинна давати однотипний результат) дає рядок . Це можна побачити в наступному результаті:

PS C: \> (ls |% {$ _. Ім'я} |? {$ _. Містить ("og")}).
3
PS C: \> (ls |% {$ _. Ім'я} |? {$ _. Містить ("Прог")}).
13

Це може дуже дратувати, оскільки, мабуть, є менше папок, які відповідають 'og', ніж ті, що відповідають 'Prog'.

Очевидно, що PowerShell неявно «розв’язує» масив з одним предметом до одного об’єкта, і ми ніколи не отримуємо масив довжиною 1. Схоже, що кожного разу, коли мені хочеться підрахувати результати, що надходять по конвеєру, я повинен перевірити, чи я м мати справу з масивом чи ні.

Як я можу запобігти цьому? Як ти з цим справляється?


Це з StackOverflow може допомогти: stackoverflow.com/questions/1827862 / ... stackoverflow.com/questions/1390782 / ... Якщо ви не пекуче до $_.Contains, то %{,,$_.Name}працює ...
Боб

Відповіді:


56

Очевидно, що PowerShell неявно "розпаковує" масив з одним предметом до одного об'єкта,

І нульовий елемент призводить до $null.

Як я можу запобігти цьому?

Ви не можете.

Як ти з цим справляється?

Використовуйте конструктор масиву ( @(...)), щоб змусити колекцію (можливо, з нулем або одним елементом) повернути:

$res = @(ls | %{$_.Name} | ?{$_.Contains("Prog")})

Дякую, це ідеально! Я піднесу пропозицію, як тільки у мене буде 15 репутації.
сир ТАК перестань шкодити Моніці

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.
ПАМ'ЯТЬ

Так, дефіс все ще присутній
Джеймс Вісман

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, а не трубопровід.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.