web-dev-qa-db-fra.com

Comment obtenir Select-Object pour renvoyer un type brut (par exemple String) plutôt que PSCustomObject?

Le code suivant me donne un tableau de PSCustomObjects, comment puis-je le faire retourner un tableau de chaînes?

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

(Comme question secondaire, à quoi sert la partie psiscontainer? J'ai copié cela à partir d'un exemple en ligne)

Post-Accept Edit: Deux bonnes réponses, j'aimerais pouvoir les marquer toutes les deux. Ont attribué la réponse originale.

37
Mark Ingram

Il vous suffit de sélectionner la propriété que vous souhaitez parmi les objets. FullName dans ce cas.

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

Edit: Explication pour Mark, qui demande: "Que fait le foreach? Qu'est-ce que l'énumération?"

L'explication de Sung Meister est très bonne, mais je vais ajouter une procédure pas à pas ici car elle pourrait être utile.

Le concept clé est le pipeline. Imaginez une série de balles de ping-pong qui roulent dans un tube étroit l'une après l'autre. Ce sont les objets dans le pipeline. Chaque étape du pipeline - les segments de code séparés par des caractères pipe (|) - a un tube qui y pénètre et un tube qui en sort. La sortie d'un étage est connectée à l'entrée de l'étage suivant. Chaque étape prend les objets à leur arrivée, leur fait des choses et les renvoie dans le pipeline de sortie ou envoie de nouveaux objets de remplacement.

Get-ChildItem $directory -Recurse

Get-ChildItem parcourt le système de fichiers en créant des objets FileSystemInfo qui représentent chaque fichier et répertoire qu'il rencontre et les place dans le pipeline.

Select-Object FullName

Select-Object prend chaque objet FileSystemInfo à son arrivée, en récupère la propriété FullName (qui est un chemin dans ce cas), place cette propriété dans un tout nouvel objet personnalisé qu'il a créé et place cet objet personnalisé dans le pipeline.

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

Ceci est un filtre. Il prend chaque objet, l'examine et le renvoie ou le jette selon certaines conditions. Soit dit en passant, votre code contient un bogue. Les objets personnalisés qui arrivent ici n'ont pas de propriété psiscontainer. Cette étape ne fait rien. Le code de Sung Meister est meilleur.

foreach {$_.FullName}

Foreach, dont le nom long est ForEach-Object, récupère chaque objet à son arrivée, et ici, récupère la propriété FullName, une chaîne, de celui-ci. Maintenant, voici la partie subtile: Toute valeur qui n'est pas consommée, c'est-à-dire qui n'est pas capturée par une variable ou supprimée d'une manière ou d'une autre, est placée dans le pipeline de sortie. À titre expérimental, essayez de remplacer cette étape par ceci:

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

Essayez-le et examinez la sortie. Il y a quatre valeurs dans ce bloc de code. Aucun d'eux n'est consommé. Notez qu'ils apparaissent tous dans la sortie. Maintenant, essayez ceci:

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

Notez qu'une des valeurs est capturée par une variable. Il n'apparaît pas dans le pipeline de sortie.

37
dan-gph

Pour obtenir la chaîne du nom de fichier, vous pouvez utiliser

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

Le paramètre -ExpandProperty Vous permet de récupérer un objet en fonction du type de propriété spécifié.

Des tests supplémentaires montrent que cela ne fonctionnait pas avec V1, mais que la fonctionnalité est fixe à partir du V2 CTP3.

24
Steven Murawski

Pour la question n ° 1

J'ai supprimé la partie "select-object" - elle est redondante et déplacée "où" filtre avant "foreach" contrairement à réponse de dangph - Filtrez dès que possible afin de ne traiter qu'un sous-ensemble de ce que vous avoir à traiter dans le prochain pipeline.

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

Cet extrait de code lit essentiellement

  • Récupère récursivement tous les fichiers chemin complet de tous les fichiers (répertoire Get-ChildItem $ -Recurse)
  • Filtrer les répertoires (Where-Object {! $ _. PsIsContainer})
  • Renvoyer le nom de fichier complet uniquement (pour chaque {$ _. FullName})
  • Enregistrez tous les noms de fichiers dans $ files

Notez que pour foreach {$ _. FullName}, dans powershell, la dernière instruction dans un bloc de script ({...}) est retournée, dans ce cas $ _. FullName de type chaîne

Si vous avez vraiment besoin d'obtenir un objet brut, vous n'avez rien à faire après vous être débarrassé de "select-object". Si vous deviez utiliser Select-Object mais souhaitez accéder à un objet brut, utilisez "PsBase", qui est une question totalement différente (rubrique) - Reportez-vous à " Quoi de neuf avec PSBASE, PSEXTENDED, PSADAPTED et PSOBJECT? "pour plus d'informations sur ce sujet

Pour la question n ° 2

Et aussi le filtrage par ! $ _. PsIsContainer signifie que vous excluez des objets de niveau conteneur - Dans votre cas, vous faites Get-ChildItem sur un = FileSystem fournisseur (vous pouvez voir les fournisseurs PowerShell via Get-PsProvider), donc le conteneur est un DirectoryInfo (dossier)

PsIsContainer signifie différentes choses sous différents fournisseurs PowerShell; par exemple) Pour le fournisseur Registry, PsIsContainer est de type Microsoft.Win32.RegistryKey Essayez ceci:

>pushd HKLM:\SOFTWARE
>ls | gm

[MISE À JOUR] à la question suivante: Que fait le foreach? Qu'est-ce que l'énumération? Pour clarifier, "foreach" est un alias pour "Foreach-Object" Vous pouvez le découvrir à travers,

get-help foreach

-- ou --

get-alias foreach

Maintenant, dans ma réponse, "foreach" énumère chaque instance d'objet de type FileInfo retourné par le canal précédent (qui a filtré les répertoires). FileInfo a une propriété appelée FullName et c'est ce que "foreach" énumère.
Et vous référencez un objet passé par le pipeline via une variable de pipeline spéciale appelée "$ _" qui est de type FileInfo dans le contexte de bloc de script "foreach".

9
dance2die

Pour la V1, ajoutez le filtre suivant à votre profil:

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

Ensuite, vous pouvez le faire:

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

BTW, si vous utilisez PowerShell Community Extensions vous l'avez déjà.

En ce qui concerne la possibilité d'utiliser Select-Object -Expand dans V2, c'est une astuce mignonne mais pas évidente et ce n'est vraiment pas ce à quoi Select-Object ni -Expand étaient destinés. -Expand est tout sur l'aplatissement comme SelectMany de LINQ et Select-Object concerne la projection de plusieurs propriétés sur un objet personnalisé.

4
Keith Hill