Domanda

L'ultima volta mi sono confuso A proposito PowerShell srotola con entusiasmo le raccolte, Keith ha riassunto la sua euristica in questo modo:

Inserimento dei risultati (un array) all'interno di un'espressione di raggruppamento (o sottoespressione, ad es.$()) lo rende nuovamente idoneo allo srotolamento.

Ho preso a cuore questo consiglio, ma mi ritrovo ancora incapace di spiegare alcuni aspetti esoterici.In particolare, l'operatore Format non sembra rispettare le regole.

$lhs = "{0} {1}"

filter Identity { $_ }
filter Square { ($_, $_) }
filter Wrap { (,$_) }
filter SquareAndWrap { (,($_, $_)) }

$rhs = "a" | Square        
# 1. all succeed
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

$rhs = "a" | Square | Wrap       
# 2. all succeed
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

$rhs = "a" | SquareAndWrap       
# 3. all succeed
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

$rhs = "a", "b" | SquareAndWrap       
# 4. all succeed by coercing the inner array to the string "System.Object[]"
$lhs -f $rhs
$lhs -f ($rhs)
$lhs -f $($rhs)
$lhs -f @($rhs)

"a" | Square | % {
    # 5. all fail
    $lhs -f $_
    $lhs -f ($_)
    $lhs -f @($_)
    $lhs -f $($_)            
}

"a", "b" | Square | % {
    # 6. all fail
    $lhs -f $_
    $lhs -f ($_)
    $lhs -f @($_)
    $lhs -f $($_)            
}

"a" | Square | Wrap | % {
    # 7. all fail
    $lhs -f $_
    $lhs -f ($_)
    $lhs -f @($_)
    $lhs -f $($_)            
}

"a", "b" | Square | Wrap | % {
    # 8. all fail
    $lhs -f $_
    $lhs -f ($_)
    $lhs -f @($_)
    $lhs -f $($_)            
}

"a" | SquareAndWrap | % {
    # 9. only @() and $() succeed
    $lhs -f $_
    $lhs -f ($_)
    $lhs -f @($_)
    $lhs -f $($_)            
}

"a", "b" | SquareAndWrap | % {
    # 10. only $() succeeds
    $lhs -f $_
    $lhs -f ($_)
    $lhs -f @($_)
    $lhs -f $($_)            
}

Applicando gli stessi schemi che abbiamo visto nella domanda precedente, è chiaro perché i casi come #1 e #5 si comportano in modo diverso:l'operatore della pipeline segnala al motore di script di svolgere un altro livello, mentre l'operatore di assegnazione no.In altre parole, tutto ciò che si trova tra due | viene trattato come un'espressione raggruppata, proprio come se fosse all'interno di ().

# all of these output 2
("a" | Square).count                       # explicitly grouped
("a" | Square | measure).count             # grouped by pipes
("a" | Square | Identity).count            # pipe + ()
("a" | Square | Identity | measure).count  # pipe + pipe

Per lo stesso motivo, il caso n. 7 non rappresenta alcun miglioramento rispetto al caso n. 5.Qualsiasi tentativo di aggiungere un extra Avvolgere verrà immediatamente sovvertito dal tubo in più.Idem #8 contro #6.Un po' frustrante, ma fino a questo punto sono totalmente d'accordo.

Domande rimanenti:

  • Perché il caso n. 3 non subisce la stessa sorte del caso n. 4? $rhs dovrebbe contenere l'array nidificato (,("aa")) ma il suo livello esterno si sta svolgendo... da qualche parte...
  • Cosa sta succedendo con i vari operatori di raggruppamento nei numeri 9-10?Perché si comportano in modo così irregolare e perché sono necessari?
  • Perché i guasti nel caso n. 10 non si degradano con grazia come fa il n. 4?
È stato utile?

Soluzione

Beh, c'è sicuramente un bug in questo.(Ho appena scritto una pagina sul PoshCode Wiki a riguardo ieri, in realtà, e c'è un bug sulla connessione).

Prima le risposte, poi le domande:

Per ottenere un comportamento coerente dagli array con il file -f formattazione della stringa, dovrai assicurarti al 100% che siano PSObject.Il mio suggerimento è di farlo al momento dell'assegnazione.È ipotetico essere eseguito automaticamente da PowerShell, ma per qualche motivo non viene eseguito finché non si accede a una proprietà o qualcosa del genere (come documentato in quello pagina wiki E insetto).Per esempio.( <##> è il mio suggerimento):

<##> $a = 1,2,3
<##> "$a"
1 2 3

<##> $OFS = "-"  # Set the Output field separator
<##> "$a"
1-2-3

<##> "{0}" -f $a
1 

<##> $a.Length
3 

<##> "{0}" -f $a
1-2-3

# You can enforce correct behavior by casting:
<##> [PSObject]$b = 1,2,3
<##> "{0}" -f $a
1-2-3

Nota che dopo averlo fatto, NON verranno srotolati quando si passa a -f ma piuttosto verranno emessi correttamente, come sarebbero se inserisci direttamente la variabile nella stringa.

Perché il caso n. 3 non subisce la stessa sorte del caso n. 4?$rhs dovrebbe contenere l'array annidato (,("a", "a")) ma il suo livello esterno si sta srotolando... da qualche parte...

La versione semplice della risposta è che ENTRAMBI il n. 3 e il n. 4 vengono srotolati.La differenza è che in 4, il contenuto interno è un array (anche dopo che l'array esterno è stato srotolato):

$rhs = "a" | SquareAndWrap
$rhs[0].GetType()  # String

$rhs = "a","b" | SquareAndWrap
$rhs[0].GetType()  # Object[]

Cosa sta succedendo con i vari operatori di raggruppamento nei numeri 9-10?Perché si comportano in modo così irregolare e perché sono necessari?

Come ho detto prima, un array dovrebbe contare come un singolo parametro nel formato e dovrebbe essere restituito utilizzando le regole di formattazione delle stringhe di PowerShell (ad esempio:divisi da $OFS) proprio come accadrebbe se inserisci $_ direttamente nella stringa ...pertanto, quando PowerShell si comporta correttamente, $lhs -f $rhs fallirà se $lhs contiene due segnaposto.

Naturalmente, abbiamo già osservato che c'è un bug.

Comunque non vedo nulla di anomalo:@() e $() funzionano allo stesso modo per 9 e 10, per quanto posso vedere (la differenza principale, infatti, è causata dal modo in cui ForEach srotola l'array di livello superiore:

> $rhs = "a", "b" | SquareAndWrap
> $rhs | % { $lhs -f @($_); " hi " }
a a
 hi 
b b
 hi 

> $rhs | % { $lhs -f $($_); " hi " }
a a
 hi 
b b
 hi     

# Is the same as:
> [String]::Format( "{0} {1}", $rhs[0] ); " hi "
a a
 hi 

> [String]::Format( "{0} {1}", $rhs[1] ); " hi "
b b
 hi     

Quindi vedi che il bug è che @() o $() faranno sì che l'array venga passato come [object[]] alla chiamata al formato stringa invece che come PSObject che ha valori speciali a-string.

Perché i guasti nel caso n. 10 non si degradano con grazia come fa il n. 4?

Questo è fondamentalmente lo stesso bug, in una manifestazione diversa.Le matrici non dovrebbero mai apparire come "System.Object[]" in PowerShell a meno che non si chiami manualmente il loro nativo .ToString() metodo o passarli direttamente a String.Format() ...il motivo per cui lo fanno al punto 4 è che bug: PowerShell non è riuscito a estenderli come PSOjbect prima di passarli alla chiamata String.Format.

Puoi vederlo se accedi a una proprietà dell'array prima di passarlo o eseguirne il cast su PSObject come nei miei esempi originali.Tecnicamente, gli errori nel numero 10 sono l'output corretto:stai passando solo UNA cosa (un array) a string.format, quando si aspettava DUE cose.Se cambiassi $lhs solo in "{0}" vedresti l'array formattato con $OFS


Mi chiedo però, che comportamento fai tu Piace e quale pensi che sia? corretto, considerando il mio primo esempio?Penso che l'output separato da $ OFS sia corretto, al contrario di srotolare l'array come accade se lo @(avvolgi) o lo trasmetti a [object[]] (Per inciso, nota cosa succede se lo trasmetti a [int[ ]] è un diverso comportamento bacato):

> "{0}" -f [object[]]$a
1

> "{0}, {1}" -f [object[]]$a  # just to be clear...
1,2

>  "{0}, {1}" -f [object[]]$a, "two"  # to demonstrate inconsistency
System.Object[],two

> "{0}" -f [int[]]$a
System.Int32[]

Sono sicuro che molti script siano stati scritti inconsapevolmente sfruttando questo bug, ma mi sembra comunque abbastanza chiaro che lo svolgimento che sta avvenendo nel giusto per essere chiari L'esempio NON è il comportamento corretto, ma si verifica perché, durante la chiamata (all'interno del core di PowerShell) a .Net String.Format( "{0}", a ) ... $a è un object[] che è ciò che String.Format si aspettava come parametro Params ...

Penso che questo debba essere risolto.Se si desidera mantenere la "funzionalità" di srotolare l'array, è necessario utilizzare l'operatore @ splatting, giusto?

Altri suggerimenti

Né Piazza Non avvolgere farà quello che stai cercando in s # '5 e 7. Indipendentemente dal fatto che si mette una matrice all'interno di un'espressione di raggruppamento () come si fa in piazza o si utilizza l'operatore virgola come si fa in Wrap, quando si utilizzano queste funzioni in cantiere la loro produzione viene srotolato come si imbocca alla fase successiva gasdotto uno alla volta. Allo stesso modo in 6 e 8, non importa che si pipa in più oggetti, sia Square e Wrap verrà loro da mangiare fuori uno alla volta per la vostra fase foreach.

Casi 9 e 10 sembrano indicare un bug in PowerShell. Prendete questo frammento modificato e provarlo:

"a" | SquareAndWrap | % {    
    # 9. only @() and $() succeed  
    $_.GetType().FullName
    $_.Length
    $lhs -f [object[]]$_
    $lhs -f [object[]]($_)    
    $lhs -f @($_)   
    $lhs -f $($_)            
}

Funziona. Essa mostra inoltre che l'alreadyd foreach riceve un oggetto [] dimensione 2 così $_ dovrebbe funzionare senza fusione alla [oggetto []] o confezionati in una sottoespressione o matrice sottoespressione. Abbiamo visto alcuni bug relativi alla V2 psobjects non scartare in modo corretto e questo sembra essere un altro esempio di questo. Se scartare i psobject manualmente funziona per esempio $_.psobject.baseobject.

I "penso" che cosa si sono riprese per in Wrap è questa:

function Wrap2 { Begin {$coll = @();} Process {$coll += $_} End {,$coll} }

Questo accumulerà tutta input pipeline e poi uscita come una singola matrice. Questo lavoro per caso 8, ma è comunque necessario lanciare a [oggetto []] sui primi due usi dell'operatore -f.

A proposito delle parentesi sia Square e Wrap e le parentesi esterni in SquareAndWrap sono inutili.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top