3 votes

<blockquote>Get-ChildItem répond différemment lorsque la sortie de New-Item est envoyée vers null</blockquote>

Pourquoi ces deux extraits de code se comportent-ils différemment? Est-ce un bug? (PSVersion = 5.1.22621.2506, PSEdition = Desktop)

## Extrait de code #1
rmdir -recurse org
mkdir org\dir1
Get-ChildItem org -Directory  | foreach {   $_.GetType() }

vs

## Extrait de code #2
rmdir -recurse org
mkdir org\dir1 | Out-Null
Get-ChildItem org -Directory  | foreach {   $_.GetType() }

La seule différence entre ces deux est que dans l'extrait 2, la sortie de mkdir est redirigée vers Out-Null. Ce deuxième extrait (et ses variantes comme $null = mkdir org/dir1) fonctionnent comme je m'y attendais:

Get-ChildItem org -Directory  | foreach {   $_.GetType() }

IsPublic IsSerial Name                                     BaseType                                                                  
-------- -------- ----                                     --------                                                                  
True     True     DirectoryInfo                            System.IO.FileSystemInfo

Mais lorsque la sortie de mkdir n'est pas capturée (comme dans l'extrait 1), Get-ChildItem fait quelque chose d'inattendu:

Get-ChildItem org -Directory  | foreach {   $_.GetType() }

    Directory: C:\Users\Phil\ps\as1\org

Mode                 LastWriteTime         Length Name                                                             
----                 -------------         ------ ----
d-----         4/10/2024  12:56 AM                dir1                                  

Module                     : CommonLanguageRuntimeLibrary
Assembly                   : mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
TypeHandle                 : System.RuntimeTypeHandle
DeclaringMethod            : 
BaseType                   : System.IO.FileSystemInfo
UnderlyingSystemType       : System.IO.DirectoryInfo
FullName                   : System.IO.DirectoryInfo
AssemblyQualifiedName      : System.IO.DirectoryInfo, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
[: : :]

Les mêmes résultats sont observés si vous utilisez New-Item au lieu de mkdir. Je ne comprends pas pourquoi (ou même comment) une structure de répertoire peut être différente en fonction de la capture de sa création.

2voto

mklement0 Points 12597

Le comportement - qui n'est qu'un problème d'affichage - peut être surprenant, mais il est conçu ainsi :

Si le premier objet de sortie dans un pipeline déclenche une mise en forme d'affichage basée sur un tableau en fonction des définitions de mise en formeexplicitement définies (voir ci-dessous pour comprendre pourquoi cela importe), les objets de sortie ultérieurs de _type différent_sont forcés à une mise en forme en liste.[1]

Plus précisément, l'objet de sortie de mkdir - une instance de System.IO.DirectoryInfo - a déclenché la mise en forme de type Format-Table, car une vue en tableau (avec des colonnes prédéfinies) est définie comme la vue par défaut dans les définitions de mise en forme associées à ce type.

Par conséquent, les instances de System.Type ultérieures produites par les appels à .GetType() dans votre Get-ChildItemont été implicitement forcées d'utiliser la mise en forme Format-List.

Au détriment de la production de données de sortie à partir de votre script, vous pouvez appliquer une mise en forme explicite à chaque commande. Dans le cas le plus simple, redirigez-la vers Out-Host, qui applique la mise en forme par défaut de chaque commande de manière isolée et l'envoie directement à l'hôte, l'empêchant d'être capturé ; ou utilisez des appels de cmdlet Format-* explicitement; bien qu'ils produisent une sortie pouvant être capturée, elle n'est plus des données - ce sont des objets représentant des instructions de mise en forme (c'est pourquoi les cmdlets Format-* ne devraient jamais être utilisés que pour une sortie à afficher).


Supposément, la raison de conception sous-jacente à ce comportement est :

  • La mise en forme d'affichage tabulaire de PowerShell est conçue pour des objets du même type.

  • Si les objets suivants sont de types différents, ils peuvent avoir des propriétés complètement différentes qui ne contribuent pas de valeurs aux colonnes du tableau. Afin de ne pas rendre ces objets effectivement invisibles, la mise en forme en liste[1] est activée dès réception du premier objet d'un type différent.

    • Malheureusement, si l'initialisation du format en tableau ne possède pas de [2] définitions de mise en forme associées[2] et déclenche seulement la mise en forme du tableau incidemment, en raison d'avoir 4 propriétés ou moins , les objets suivants qui ne partagent aucune propriété deviennent effectivement invisibles ; par exemple :

       # !! La sortie de Get-Item est *invisible*, car l'objet [pscustomobject]
       # déclenche incidemment une vue en tableau avec juste une colonne 'foo'.
       [pscustomobject] @{ foo=1 }; Get-Item .
    • Ce comportement problématique est l'objet du problème de GitHub n°7871 et du problème de GitHub n°12825.


[1] En termes stricts, si un objet donné a des données de mise en forme associées qui définissent une vue personnalisée comme son défaut, cette dernière est utilisée ; un exemple est la sortie de Get-Date.

[2] Pour un objet donné, vous pouvez utiliser Get-FormatData pour vérifier si son type a des définitions de mise en forme associées ; par exemple (une sortie non vide implique la présence de définitions) :
$obj = Get-Item .; Get-FormatData -TypeName $obj.GetType().FullName

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X