2011-01-15 34 views
10

Tôi đang cố gắng viết một số chức năng PowerShell thực hiện một số công cụ và sau đó gọi một cách rõ ràng đến các chức năng tích hợp sẵn có. Tôi muốn vượt qua tất cả các đối số bị ảnh hưởng. Tôi không muốn biết bất kỳ chi tiết nào về các lập luận.Làm cách nào để chuyển 'dòng đối số' của một chức năng PowerShell sang một chức năng khác?

Tôi mệt mỏi khi sử dụng 'splat' để làm điều này với @args nhưng điều đó không hoạt động như tôi mong đợi.

Trong ví dụ bên dưới, tôi đã viết chức năng đồ chơi có tên là myls, nghĩa vụ in xin chào! và sau đó gọi cùng một chức năng được tích hợp sẵn, Get-ChildItem, rằng lệnh gọi ls bí danh dựng sẵn có phần còn lại của dòng đối số vẫn còn nguyên vẹn. Những gì tôi có cho đến nay hoạt động khá tốt:

function myls 
{ 
    Write-Output "hello!" 
# $MyInvocation | Format-List   # <-- uncomment this line for debug info 
    Invoke-Expression ("Get-ChildItem " + $MyInvocation.UnboundArguments -join " ") 
} 

Một phiên bản đúng của myls nên có thể xử lý được gọi mà không có lý lẽ, với một đối số, với các đối số được đặt tên, từ một dòng chứa nhiều lệnh dấu chấm phẩy phân và với các biến trong các đối số, bao gồm các biến chuỗi chứa dấu cách. Về cơ bản, nó phải là một lựa chọn thay thế cho ls.

Các thử nghiệm dưới đây so sánh myls và được xây dựng trong ls:

[Chú ý: sản lượng elided và/hoặc đầm để tiết kiệm không gian]

PS> md C:\p\d\x, C:\p\d\y, C:\p\d\"jay z" 
PS> cd C:\p\d 
PS> ls         # no args 
PS> myls        # pass 
PS> cd .. 
PS> ls d        # one arg 
PS> myls d        # pass 
PS> $a="A"; $z="Z"; $y="y"; $jz="jay z" 
PS> $a; ls d; $z      # multiple statements 
PS> $a; myls d; $z      # pass 
PS> $a; ls d -Exclude x; $z   # named args 
PS> $a; myls d -Exclude x; $z   # pass 
PS> $a; ls d -Exclude $y; $z   # variables in arg-line 
PS> $a; myls d -Exclude $y; $z   # pass 
PS> $a; ls d -Exclude $jz; $z   # variables containing spaces in arg-line 
PS> $a; myls d -Exclude $jz; $z  # FAIL! 

Có cách nào tôi có thể viết lại myls để có được hành vi tôi muốn?

Câu trả lời ngắn: Có, có thể. Tin xấu: nó yêu cầu mã biết chi tiết về các tham số và siêu dữ liệu khác về hàm mà người dùng muốn gọi đến. Tin tốt: người ta không cần phải viết tất cả những người này. Siêu dữ liệu này có sẵn theo chương trình và tồn tại các mô-đun có sẵn mà người dùng có thể sử dụng để tự động tạo mã proxy của bộ xương (xem câu trả lời của @ Jaykul bên dưới). Tôi chọn sử dụng the module named "MetaProgramming". Khi nhập khẩu, tạo ra một thả trong myls kịch bản là chết đơn giản:

New-ProxyCommand ls > .\myls.ps1

Sau đó, người ta có thể bắt đầu tùy chỉnh mới được tạo myls.ps1 kịch bản, như thế này:

... 
    begin 
    { 
    Write-Output "hello!"    # <-- add this line 
    try { 
     $outBuffer = $null 
    ... 

Voila! Phiên bản mới này vượt qua tất cả các bài kiểm tra.

Trả lời

4

Nếu bạn muốn có trình bao bọc thả cho ls, bạn nên viết proper proxy function. Có một vài phiên bản của trình tạo trên PoshCode.org, bao gồm the one from Lee Holmes' PowerShell Cookbook

+0

bạn đúng, đây là những gì tôi thực sự cần. Cảm ơn! – jwfearn

+0

Nói cách khác: sao chép tự động. Chỉ đáng kinh ngạc. – alecov

+2

Nếu bạn có thể sao chép-dán, bạn sẽ không cần tạo mã. Những gì OP đang làm là cố gắng phân lớp lệnh: viết một lệnh mới dựa trên bản gốc, nhưng với các sửa đổi tùy chỉnh ... và trong tập lệnh. Nếu bạn đã làm nó trong C#, bạn chỉ cần kế thừa.Nhưng bạn đang ở trong PowerShell, vì vậy bạn phải viết một hàm bắt đầu bằng cách sao chép tập hợp các tham số từ lệnh ghép ngắn ban đầu (trong PowerShell, chúng tôi gọi các hàm proxy đó). Nếu có bất cứ điều gì mà khóc cho thế hệ mã, đây là nó. – Jaykul

2

Tôi tin rằng điều này:

function myls {Write-Output "hello!"; iex "Get-ChildItem @args"}

sẽ đến gần hơn để tạo ra kết quả mong đợi.

Cập nhật: Có vẻ là một lỗi được biết đến với việc sử dụng @args theo cách này để vượt qua tên các thông số:

https://connect.microsoft.com/PowerShell/feedback/details/368512/splat-operator-args-doesnt-properly-pass-named-arguments?wa=wsignin1.0

Tôi muốn ngừng sử dụng đó cho đến khi nó được giải quyết.

+0

Nó gần hơn - nó không tạo ra lỗi - tiếc là nó cũng bỏ qua arg. Nếu bạn thử nghiệm ở trên, kết quả đầu ra của 'myls' và' myls -Exclude b' là, không chính xác, giống nhau. – jwfearn

+0

Hãy thử nó mà không có dòng đầu tiên đó. – mjolinor

4

Làm cho trình bao bọc đúng cách không phải là dễ dàng, thật không may. Trước hết, toán tử splat có thể được áp dụng cho một hashtable (ví dụ: PSBoundParameters tự động) hoặc khác, không trực tiếp đến mảng $args.

Có ít nhất hai tùy chọn (cả hai đều không hoàn hảo) và bản hack (xem phần cập nhật).

Tùy chọn 1. Sử dụng cách "cổ điển" để tạo trình bao bọc. Ví dụ: xem cách này được thực hiện cho các chức năng help mà là một wrapper của Get-Help cmdlet:

Get-Content function:\help 

Bạn có thể thấy rằng các chức năng bao bọc khai hải tất cả các thông số mà cmdlet bọc có để có mà PSBoundParametersđược giới hạn theo các tham số chức năng hiện tại.

Ví dụ của bạn. Nếu hàm của bạn khai báo tham số Exclude thì mã ví dụ bắt đầu hoạt động. Nhưng nó hoạt động cho Exclude chỉ, không Force, mặc dù Force cũng được thông qua tại:

function myls($Exclude) { 
    # only Exclude is in $PSBoundParameters even though we send Force, too: 
    $PSBoundParameters | Out-String | Out-Host 
    Write-Output "hello!" 
    Get-ChildItem @PSBoundParameters 
} 
cd d 
myls -Exclude b -Force 

Lựa chọn 2. Về lý thuyết trong ta có thể xây dựng một Hashtable từ mảng $args bằng tay và áp dụng các nhà điều hành splat với nó. Nhưng nhiệm vụ này trông không hấp dẫn lắm.


CẬP NHẬT

Vâng, có thực sự là có một ad-hoc tùy chọn (tinh khiết Hack!) Mà đòi hỏi nỗ lực tối thiểu và sẽ làm việc trong một số tình huống không đáng kể, chủ yếu là tương tác,. Nó không phải là cho mã nào đó nghiêm trọng!

function myls { 
    # extra job 
    Write-Output "hello!" 

    # invoke/repeat the calling code line with myls "replaced" with Get-ChildItem 
    Set-Alias myls Get-ChildItem 
    Invoke-Expression $MyInvocation.Line 
} 

cd d 

# variables can be used as the parameter values, too 
$exclude = 'b' 

myls -Exclude $exclude -Force 
+0

Các công trình hack và nó có lợi thế mà người ta không cần phải xử lý (hoặc thậm chí biết về) tất cả các thông số tùy chọn khác nhau. – jwfearn

+1

Vâng, nó hoạt động nhưng không đủ tốt để sử dụng thực tế. '$ MyInvocation.Line' là toàn bộ dòng gọi bao gồm các câu lệnh khác. Kết quả là, điều này không hoạt động: '$ result = myls ...'. Nhưng điều này dường như làm việc: 'myls ... -OutVariable result'. –

+0

Vâng, tôi cũng nhận thấy điều đó. Phải có một sửa chữa. Tôi sẽ cho bạn biết nếu tôi tìm thấy một. – jwfearn

Các vấn đề liên quan