2016-07-13 13 views
18

Tôi đang cố gắng sử dụng LINQ trong PowerShell. Có vẻ như điều này hoàn toàn có thể xảy ra vì PowerShell được xây dựng trên nền .NET Framework, nhưng tôi không thể làm cho nó hoạt động được. Ví dụ, khi tôi thử như sau (giả tạo) mã:Có thể sử dụng LINQ trong PowerShell không?

$data = 0..10 

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

tôi nhận được lỗi sau:

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

Đừng bận tâm sự thật rằng điều này có thể được thực hiện với Where-Object. Điểm của câu hỏi này không phải là tìm một cách thành ngữ để thực hiện thao tác này trong PowerShell. Một số nhiệm vụ sẽ dễ dàng hơn trong nhiều năm ánh sáng trong PowerShell nếu tôi có thể sử dụng LINQ.

+0

cao nhất bình chọn câu trả lời cho câu hỏi này sẽ đề nghị rằng bạn không thể sử dụng các toán LINQ trong PowerShell http://stackoverflow.com/questions/2869967/how-to-query -list-in-powershell – DeanOC

+1

'[System.Linq.Enumerable] :: Ở đâu ($ data, [Func [object, bool]] {param ($ x) $ x -gt 5})' – PetSerAl

+1

@PetSerAl Hãy nói cái gì! Bạn thật tuyệt! Bạn có thể thêm nó như là một câu trả lời và bạn có thể giải thích tại sao nó hoạt động nhưng không: '[System.Linq.Enumerable] :: Ở đâu ($ data, [System.Func [int, bool]] {param ($ x) $ x -gt 5}) ', không. Giả sử bạn biết tại sao, đó là. –

Trả lời

23

Sự cố với mã của bạn là PowerShell không thể quyết định loại đại biểu cụ thể nào là cá thể ScriptBlock ({ ... }) sẽ được truyền. Vì vậy, nó không thể chọn một instantiation delegate loại bê tông cho tham số 2 chung của Where method. Và nó cũng không có cú pháp để chỉ định một tham số chung một cách rõ ràng. Để giải quyết vấn đề này, bạn cần phải cast dụ ScriptBlock để các đại biểu đúng loại mình:

$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?

Bởi vì bạn $data[object[]], không [int[]], cho rằng PowerShell tạo [object[]] mảng theo mặc định; bạn có thể, tuy nhiên, xây dựng [int[]] trường một cách rõ ràng:

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

Để bổ sung PetSerAl's helpful answer với một câu trả lời rộng hơn để phù hợp với tiêu đề chung của câu hỏi:

Sử dụng LINQ trong PowerShell:

  • Bạn cần PowerShell v3 hoặc cao hơn.

  • Bạn không thể gọi các phương pháp khuyến nông LINQ trực tiếp trên trường bộ sưu tập và thay vào đó phải gọi các phương thức LINQ như phương pháp tĩnh của [System.Linq.Enumerable] loại mà bạn vượt qua bộ sưu tập đầu vào như là đối số đầu tiên.

    • Phải làm như vậy mất đi sự lỏng của API LINQ, bởi vì phương pháp chaining là không còn một lựa chọn. Thay vào đó, bạn phải làm tổ cuộc gọi tĩnh, theo thứ tự ngược lại.

    • Ví dụ:, Thay vì $inputCollection.Where(...).OrderBy(...) bạn phải viết [Linq.Enumerable]::OrderBy([Linq.Enumerable]::Where($inputCollection, ...), ...)

  • chức năng trợ giúp và các lớp:

    • Một số phương pháp, chẳng hạn như .Select(), có thông số mà chấp nhận chung Func<> đại biểu (ví dụ, Func<T,TResult> có thể được tạo bằng mã PowerShell, thông qua một đoạn mã được áp dụng cho khối tập lệnh; ví dụ:
      [Func[object, bool]] { $Args[0].ToString() -eq 'foo' }

      • Các tham số kiểu generic đầu tiên của Func<> đại biểu phải phù hợp với loại các yếu tố của bộ sưu tập đầu vào; hãy nhớ rằng PowerShell tạo ra các mảng [object[]] theo mặc định.
    • Một số phương pháp, chẳng hạn như .Contains().OrderBy có thông số mà chấp nhận đối tượng mà thực hiện các giao diện cụ thể như IEqualityComparer<T>IComparer<T>; ngoài ra, các loại đầu vào có thể cần phải triển khai IEquatable<T> để các so sánh hoạt động như dự định, chẳng hạn như với .Distinct(); tất cả các số này yêu cầu các lớp được biên dịch được viết, thông thường, trong C# (mặc dù bạn có thể tạo chúng từ PowerShell bằng cách chuyển một chuỗi có mã C# được nhúng vào lệnh ghép ngắn Add-Type); Tuy nhiên, trong PSv5 +, bạn cũng có thể sử dụng tùy chỉnh PowerShell classes, với một số hạn chế.

  • phương pháp Generic:

    • Một số LINQ phương pháp tự mang tính tổng quát và do đó đòi hỏi một tham số kiểu; PowerShell không thể trực tiếp gọi các phương thức như vậy và phải sử dụng sự phản chiếu thay thế; ví dụ .:

      # 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 
      
  • Các phương pháp LINQ trả về một iterator chứ không phải là một bộ sưu tập thực tế.

    • Trong nhiều trường hợp, tuy nhiên, bạn sẽ có thể sử dụng iterator như thể nó là một bộ sưu tập, ví dụ như khi gửi nó thông qua các đường ống dẫn.

      • Tuy nhiên, bạn không thể chỉ số vào trình lặp, cũng như bạn không thể sử dụng điều tra thành viên.
    • Nếu bạn cần kết quả dưới dạng mảng tĩnh, hãy bọc lời gọi trong [Linq.Enumerable]::ToArray(...).

      • Các phương pháp tương tự trả về các cấu trúc dữ liệu khác nhau tồn tại, chẳng hạn như ::ToList().

Đối với một dụ tiên tiến, xem this answer của tôi.
Để biết tổng quan của tất cả các phương pháp LINQ bao gồm ví dụ, hãy xem this great article.


Nói tóm lại: sử dụng LINQ từ PowerShell là cồng kềnh và là chỉ xứng đáng với nỗ lực nếu bất cứ điều nào sau đây được áp dụng:

  • bạn cần truy vấn cao cấp tính năng rằng PowerShell cmdlets của không thể cung cấp.
  • hiệu suất là tối quan trọng - xem this article
Các vấn đề liên quan