Как заставить Select-Object возвращать необработанный тип (например, String), а не PSCustomObject? - PullRequest
31 голосов
/ 06 марта 2009

Следующий код дает мне массив PSCustomObjects, как я могу получить его, чтобы он возвращал массив строк?

$files = Get-ChildItem $directory -Recurse | Select-Object FullName | Where-Object {!($_.psiscontainer)}

(В качестве дополнительного вопроса, для чего нужна часть psiscontainer? Я скопировал это из примера в Интернете)

Post-Accept Edit: Два отличных ответа, хотелось бы отметить оба. Наградили оригинальный ответ.

Ответы [ 4 ]

34 голосов
/ 06 марта 2009

Вам просто нужно выбрать желаемое свойство из объектов. FullName в этом случае.

$files = Get-ChildItem $directory -Recurse | Select-Object FullName | Where-Object {!($_.psiscontainer)} | foreach {$_.FullName}

Редактировать: Объяснение для Марка, который спрашивает: «Что делает foreach? Что это за перечисление?»

Объяснение Сун Мейстера очень хорошее, но я добавлю здесь пошаговое руководство, потому что оно может быть полезным.

Ключевой концепцией является конвейер. Представьте себе серию шариков для пинг-понга, катящихся по узкой трубе один за другим. Это объекты в конвейере. Каждый этап конвейера - сегменты кода, разделенные символом канала (|) - имеет входящий в него канал и выходящий из него канал. Выход одной ступени подключен к входу следующей ступени. Каждый этап берет объекты по мере их поступления, что-то делает с ними и отправляет их обратно в выходной конвейер или отправляет новые замещающие объекты.

Get-ChildItem $directory -Recurse

Get-ChildItem просматривает файловую систему, создавая объекты FileSystemInfo, которые представляют каждый встречающийся файл и каталог, и помещает их в конвейер.

Select-Object FullName

Select-Object берет каждый объект FileSystemInfo по мере его поступления, извлекает из него свойство FullName (в данном случае это путь), помещает это свойство в новый созданный им пользовательский объект и помещает этот пользовательский объект в трубопровод.

Where-Object {!($_.psiscontainer)}

Это фильтр. Он берет каждый объект, проверяет его и отправляет обратно или отбрасывает в зависимости от некоторых условий. Кстати, в вашем коде есть ошибка. Пользовательские объекты, которые поступают сюда, не имеют свойства psiscontainer. Этот этап на самом деле ничего не делает. Код Сена Мейстера лучше.

foreach {$_.FullName}

Foreach, длинное имя которого ForEach-Object, получает каждый объект по мере его поступления, а здесь получает свойство FullName - строку. Теперь, вот тонкая часть: любое значение, которое не используется, то есть не захватывается переменной или каким-либо образом подавляется, помещается в выходной конвейер. В качестве эксперимента попробуйте заменить этот этап следующим:

foreach {'hello'; $_.FullName; 1; 2; 3}

На самом деле попробуйте и проверьте вывод. В этом блоке кода есть четыре значения. Ни один из них не потребляется. Обратите внимание, что все они появляются в выводе. Теперь попробуйте это:

foreach {'hello'; $_.FullName; $ x = 1; 2; 3}

Обратите внимание, что одно из значений фиксируется переменной. Он не отображается в выходном конвейере.

21 голосов
/ 07 марта 2009

Чтобы получить строку для имени файла, вы можете использовать

$files = Get-ChildItem $directory -Recurse | Where-Object {!($_.psiscontainer)} | Select-Object -ExpandProperty FullName

Параметр -ExpandProperty позволяет вернуть объект в зависимости от типа указанного свойства.

Дальнейшее тестирование показывает, что это не работает с V1, но эта функциональность исправлена ​​в V2 CTP3.

8 голосов
/ 06 марта 2009

Для вопроса № 1

Я удалил часть «select-object» - она ​​избыточна и перемещена «куда» фильтр перед «foreach» в отличие от dangph's answer - Фильтруйте как можно скорее, чтобы иметь дело только с подмножеством с чем вам придется иметь дело в следующем трубопроводе.

$files = Get-ChildItem $directory -Recurse | Where-Object {!$_.PsIsContainer} | foreach {$_.FullName}

Этот фрагмент кода по сути читает

  • Рекурсивно получить полный путь ко всем файлам всех файлов (Get-ChildItem $ directory -Recurse)
  • Фильтрация каталогов (Where-Object {! $ _. PsIsContainer})
  • Возвращать только полное имя файла (foreach {$ _. FullName})
  • Сохранить все имена файлов в $ files

Обратите внимание, что для foreach {$ _. FullName} в powershell возвращается последний оператор в блоке скрипта ({...}), в данном случае $ _. FullName типа string

Если вам действительно нужно получить необработанный объект, вам не нужно ничего делать после избавления от «select-object». Если вы должны использовать Select-Object, но хотите получить доступ к необработанному объекту, используйте «PsBase», это совершенно другой вопрос (тема) - обратитесь к « Что случилось с PSBASE, PSEXTENDED, PSADAPTED и PSOBJECT?"для получения дополнительной информации по этому вопросу

На вопрос № 2

А также фильтрация по ! $ _. PsIsContainer означает, что вы исключаете объекты уровня контейнера - в вашем случае вы выполняете Get-ChildItem в FileSystem провайдера (вы можете видеть провайдеров PowerShell через Get-PsProvider), поэтому контейнером является DirectoryInfo (папка)

PsIsContainer означает разные вещи в разных провайдерах PowerShell; например, для Registry провайдера PsIsContainer имеет тип Microsoft.Win32.RegistryKey Попробуйте это:

>pushd HKLM:\SOFTWARE
>ls | gm

[ОБНОВЛЕНИЕ] на следующий вопрос: Что делает foreach? Что это за перечисление? Чтобы уточнить, "foreach" является псевдонимом для "Foreach-Object" Вы можете узнать через

get-help foreach

- или -

get-alias foreach

Теперь в моем ответе «foreach» перечисляет каждый экземпляр объекта типа FileInfo , возвращенный из предыдущего канала (с отфильтрованными каталогами). FileInfo имеет свойство с именем FullName , и это то, что перечисляет foreach.
И вы ссылаетесь на объект, переданный через конвейер через специальную переменную конвейера, называемую "$ _", которая имеет тип FileInfo в контексте блока скрипта "foreach".

4 голосов
/ 22 марта 2009

Для V1, добавьте следующий фильтр в свой профиль:

filter Get-PropertyValue([string]$name) { $_.$name }

Тогда вы можете сделать это:

gci . -r | ?{!$_.psiscontainer} | Get-PropertyName fullname

Кстати, если вы используете Расширения сообщества PowerShell , у вас уже есть это.

Что касается возможности использовать Select-Object -Expand в V2, это забавный трюк, но не очевидный и на самом деле не тот, для которого предназначен Select-Object или -Expand. -Развернуть это все о выравнивании, как в LINQ SelectMany, а Select-Object о проекции нескольких свойств на пользовательский объект.

...