2012-10-02 14 views
7

Chcę utworzyć funkcję PowerShell, która wylicza niektóre dane i uruchamia blok skryptu we wszystkich wystąpieniach.Funkcja PowerShell z sparametryzowanym blokiem skryptowym

Teraz mam (nie jest to rzeczywisty kod, ale ilustruje mój problem):

function Invoke-TenTimes 
{ 
    [CmdletBinding()] 
    param 
    (
     [Parameter(Mandatory=$true, Position=0)] 
     [ScriptBlock]$Action 
    ) 
    process 
    { 
     $digits = 0..10 
     $digits | % { 
      $Action.Invoke($_); 
     } 
    } 
} 

umieścić tę funkcję w moim module. Jednak nie otrzymam żadnego wyniku, gdy zadzwonię:

Invoke-TenTimes { $_ } 

Dane wyjściowe są puste (nic nie jest wyświetlane).

Jeśli zadzwonię

Invoke-TenTimes { $_ -eq $null } 

mam dziesięć true. W rzeczywistości widzę, że $_ ma wartość null.

Jaki jest właściwy sposób wypełniania $_?

Co doprowadza mnie do szału, jest to, że jeśli mogę umieścić tę funkcję i połączenie w tym samym pliku ps1, to działa (ale chcę przekazać blok skryptu na żądanie):

function Invoke-TenTimes 
{ 
    [CmdletBinding()] 
    param 
    (
     [Parameter(Mandatory=$true, Position=0)] 
     [ScriptBlock]$Action 
    ) 
    process 
    { 
     $digits = 0..10 
     $digits | % { 
      $Action.Invoke($_); 
     } 
    } 
} 


Invoke-TenTimes { $_ } 

Odpowiedz

2

jest to kwestia określania zakresu z przekazany w scriptblock należącego do innego zakresu niż gdzie jest wykonywany. Jeśli przekazany skrypt blokujący nie będzie musiał odwoływać się do zmiennych z jego kontekstu, możesz po prostu sklonować skrypt blokujący, aby wymusić jego zgodność z zakresem, w którym go uruchomisz. z

$action = [scriptblock]::Create($action) 

interesujące jest to, że zadziałało tylko na pierwszym miejscu przez ACCIDENT. otrzymywało $ _ z zakresu nadrzędnego miejsca, w którym znajdował się skrypt block, który był wewnątrz ciebie, foreach-object, co było dobre.

jednak wziąć swoją oryginalną wersję, a następnie uruchomić ten

"gotcha" | % { 
Invoke-TenTimes { $_ } 
} 

a zobaczysz dostaniesz haczyka 10 razy, ponieważ $ _ nie jest zerowa w zakresie rozmówców, ale jest coś.

+0

Dzięki! to rozwiązało mój problem i mogę teraz użyć bloku skryptu z $ _ :). –

3

Problem jest znany i jest specyficzny dla modułów. Być może jest to nawet błąd. Proszę spojrzeć na to pytanie: Strange behavior with Powershell scriptblock variable scope and modules, any suggestions?

Jak dla konkretnego przypadku, należy skontaktować się funkcję tak:

Invoke-TenTimes { $args[0] } 

W ten sposób powinien działać zgodnie z oczekiwaniami.

+0

To rozwiązało mój problem. Ale poczekam kilka dni, aby sprawdzić, czy ktoś ma rzeczywiste rozwiązanie do zasugerowania. –

5

Zaczynamy:

$scriptblock = {param($number) Write-Host "Current Number is $number"} 

function Invoke-TenTimes 
{ 
    [CmdletBinding()] 
    param 
    (
     [Parameter(Mandatory=$true, Position=0)] 
     [ScriptBlock]$Action 
    ) 
    $digits = 1..10 
    $digits | ForEach-Object{Invoke-Command -ScriptBlock $Action -Args $_} 
} 

Zastosowanie:

Invoke-TenTimes $scriptblock 
0

DUŻO łatwiej niż inni na to pozwalają.

Należy rozważyć, że A i B są zdefiniowane na poziomie globalnym, a tylko $ B jest zdefiniowane na poziomie modułu. Twój blok skryptu to trzeci zasięg. W tym zakresie odnosisz się do $ A i otrzymujesz wartość globalną.Podobnie, jeśli odwołasz się do $ B, otrzymasz wartość poziomu modułu, ponieważ przesłania ona globalną instancję.

PROBLEM: Jeśli spróbujesz napisać do dowolnego z nich - zostanie utworzone lokalne wystąpienie.

Odpowiedź:

([ref]$A).value = 'Something malicious' 

Zauważ, że "([ref] $ {nazwa})" jest obiektem odniesienia tak ".value" jest konieczne do osiągnięcia zmiennej rzeczywistej. Więc

([ref]$B).value.Length 

jest taka sama jak

$B.Length 

w obu przypadkach nie masz bezpośredniej kontroli nad zakresem. Dostajesz instancję, z której możesz czytać. Pierwszy przykład modyfikuje globalny $ A, a drugi przykład odwołuje się do instancji poziomu modułu $ B.

Powiązane problemy