2016-07-13 115 views
18

Sto cercando di utilizzare LINQ in PowerShell. Sembra che questo dovrebbe essere interamente possibile dal momento che PowerShell è costruito su .NET Framework, ma non riesco a farlo funzionare. Ad esempio, quando provo il seguente codice (forzato):È possibile utilizzare LINQ in PowerShell?

$data = 0..10 

[System.Linq.Enumerable]::Where($data, { param($x) $x -gt 5 }) 

ottengo il seguente errore:

Cannot find an overload for "Where" and the argument count: "2".

Non importa il fatto che questo potrebbe essere realizzato con Where-Object. Il punto di questa domanda non è trovare un modo idiomatico per eseguire questa operazione in PowerShell. Alcune attività potrebbero essere anni luce più facili da fare in PowerShell se potessi usare LINQ.

+0

La più alta votato risposta a questa domanda potrebbe suggerire che non è possibile utilizzare gli operatori LINQ in PowerShell http://stackoverflow.com/questions/2869967/how-to-query -list-in-powershell – DeanOC

+1

'[System.Linq.Enumerable] :: Dove ($ data, [Func [oggetto, bool]] {param ($ x) $ x -gt 5})' – PetSerAl

+1

@PetSerAl Dire cosa! Sei fantastico! Puoi aggiungerlo come risposta e puoi approfondire il motivo per cui funziona, ma questo: '[System.Linq.Enumerable] :: Where ($ data, [System.Func [int, bool]] {param ($ x) $ x -gt 5}) ', no. Supponendo che tu sappia il perché, cioè. –

risposta

23

Il problema con il codice è che PowerShell non può decidere a quale specifico delegato digitare l'istanza ({ ... }). Quindi non è possibile scegliere un'istanza del delegato tipo-calcestruzzo per il 2 ° parametro generico dello Where method. Inoltre, non ha la sintassi per specificare esplicitamente un parametro generico. Per risolvere questo problema, è necessario lanciare l'istanza ScriptBlock al delegato destra digitare da soli:

$data = 0..10 
[System.Linq.Enumerable]::Where($data, [Func[object,bool]]{ param($x) $x -gt 5 }) 

Why does [Func[object, bool]] work, but [Func[int, bool]] does not?

Perché il vostro $data è [object[]], non [int[]], dato che crea PowerShell [object[]] array per impostazione predefinita; È possibile, tuttavia, costruire [int[]] casi esplicitamente:

$intdata = [int[]]$data 
[System.Linq.Enumerable]::Where($intdata, [Func[int,bool]]{ param($x) $x -gt 5 }) 
4

Per completare PetSerAl's helpful answer con una risposta più ampia per abbinare titolo generico della domanda:

utilizzando LINQ in PowerShell:

  • È necessario PowerShell v3 o successivo.

  • Non è possibile chiamare i metodi di estensione LINQ direttamente sulle istanze di raccolta e invece necessario richiamare i metodi di LINQ come metodi statici del [System.Linq.Enumerable] tipo a cui si passa la raccolta di input come primo argomento.

    • vista di farlo rimuove la fluidità delle API LINQ, perché il metodo concatenamento non è un'opzione. Invece, è necessario nidificare le chiamate statiche, nell'ordine inverso.

    • E.g., Invece di $inputCollection.Where(...).OrderBy(...) è necessario scrivere [Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...)

  • Le funzioni di supporto e classi:

    • Alcuni metodi, come ad esempio .Select(), avere parametri che accettano generici Func<> delegati (ad esempio, Func<T,TResult> può essere creato utilizzando il codice PowerShell, tramite un cast applicato a un blocco di script, ad esempio:
      [Func[object, bool]] { $Args[0].ToString() -eq 'foo' }

      • Il primo parametro di tipo generico di Func<> delegati deve corrispondere al tipo degli elementi della collezione ingresso; tenere presente che PowerShell crea gli array [object[]] per impostazione predefinita.
    • Alcuni metodi, come .Contains() e .OrderBy hanno parametri che accettano oggetti che implementano interfacce specifiche, come IEqualityComparer<T> e IComparer<T>; Inoltre, i tipi di input potrebbero dover implementare IEquatable<T> affinché i confronti funzionino come previsto, ad esempio con .Distinct(); tutti questi richiedono classi compilate scritte, in genere, in C# (sebbene sia possibile crearle da PowerShell passando una stringa con codice C# incorporato al cmdlet Add-Type); in PSv5 +, tuttavia, è possibile utilizzare anche l'PowerShell classes personalizzato con alcune limitazioni.

  • metodi generici:

    • Alcuni LINQ metodi stessi sono generici e quindi richiedono un parametro di tipo; PowerShell non può chiamare direttamente chiamare tali metodi e utilizzare invece la riflessione; es .:

      # Obtain a [string]-instantiated method of OfType<T>. 
      $ofTypeString = [Linq.Enumerable].GetMethod("OfType").MakeGenericMethod([string]) 
      
      # Output only [string] elements in the collection. 
      # Note how the array must be nested for the method signature to be recognized. 
      > $ofTypeString.Invoke($null, (, ('abc', 12, 'def'))) 
      abc 
      def 
      
  • I metodi LINQ restituire un iteratore piuttosto che un effettivo incasso.

    • In molti casi, tuttavia, sarete in grado di utilizzare l'iteratore, come se si trattasse di una collezione, come ad esempio quando si invia attraverso la pipeline.

      • Tuttavia, non è possibile indice nella iteratore, né è possibile utilizzare membro enumerazione.
    • Se i risultati sono necessari come array statico, eseguire il wrapping in [Linq.Enumerable]::ToArray(...).

      • Esistono metodi simili che restituiscono strutture di dati diverse, ad esempio ::ToList().

Per un ad esempio avanzata, vedere this answer di mine.
Per una panoramica di di tutti i metodi LINQ inclusi gli esempi, vedere this great article.


In breve: utilizzando LINQ da PowerShell è ingombrante ed è solo vale la pena se una delle seguenti condizioni:

  • è necessario interrogazione funzioni avanzate che i cmdlet di PowerShell non può fornire.
  • prestazioni è di primaria importanza - vedi this article