o zakresach w programie Powershell

Aug 24 2020

Dowiaduję się o zakresach w Powershell i mam kilka pytań:

  1. O „Zakresie lokalnym”: Z tego, co przeczytałem, zakres lokalny jest zawsze zakresem bieżącym. Tak więc domyślnie, gdy tworzymy element (bez modyfikatora zakresu), np. Zmienną, w jakimś zakresie niech będzie skryptowa lub globalna, zakres będzie odpowiednio skryptowy / globalny. Więc moje pytanie brzmi: kiedy będziemy musieli jawnie określić localmodyfikator?
  2. MSDN mówi:

Możesz utworzyć nowy zakres, uruchamiając skrypt lub funkcję, tworząc sesję lub uruchamiając nowe wystąpienie programu PowerShell. Po utworzeniu nowego zakresu wynikiem jest zakres nadrzędny (zakres oryginalny) i zakres podrzędny (zakres, który utworzyłeś). ...
O ile wyraźnie nie ustawisz elementów jako prywatne, elementy w zakresie nadrzędnym są dostępne dla zakresu podrzędnego. Jednak elementy, które tworzysz i zmieniasz w zakresie podrzędnym, nie mają wpływu na zakres nadrzędny, chyba że jawnie określisz zakres podczas tworzenia elementów.

Ale kiedy spróbuję wykonać następujące czynności:

PS> $Name = "John" PS> Powershell.exe PS>echo $Name  // No Output

Z powyższego cytatu wynika, że ​​„rozpoczęcie nowej instancji programu PowerShell” jest zakresem podrzędnym, więc wszystkie elementy w zakresie nadrzędnym powinny być tam widoczne. Czy ktoś może wyjaśnić?

Odpowiedzi

2 mklement0 Aug 24 2020 at 23:16

Aby uzupełnić pomocną odpowiedź iRon :

  1. [...] kiedy będziemy musieli jawnie określić lokalny modyfikator?

$local:jest rzadko wymagany , ponieważ zakres lokalny jest implikowany w przypadku braku specyfikatora zakresu.

Jednak, że stosuje się tylko wtedy, gdy zmienna odwołuje rzeczywiście istnieje jako lokalnej zmiennej , biorąc pod uwagę, że PowerShell jest dynamiczna analiza zakresu sprawia zmienne z rodowego (rodzica) celownicze widoczny dla potomka (child) celownicze oraz (zobacz tę odpowiedź , aby uzyskać więcej informacji):

  • Na przykład, powiedzmy, że $foo = 'bar'zadeklarowałeś w zasięgu globalnym , a następnie odwołanie się do $foow skrypcie będzie najpierw szukało instancji lokalnej $foo ; jeśli go nie ma, używany jest zakres $foozdefiniowany w przodku (nadrzędnym), jeśli taki istnieje, który byłby globalny $foo w tym przykładzie i 'bar'zostałby zwrócony.

  • Z drugiej strony, jeśli w swoim skrypcie używasz $local:foo, bez $foozdefiniowania zmiennej lokalnej , otrzymujesz albo $nulldomyślnie, albo, jeśli obowiązuje Set-StrictMode -Version 2lub wyższy, wystąpi błąd kończący instrukcję .


  1. MSDN mówi: [...] tworząc sesję lub uruchamiając nowe wystąpienie programu PowerShell [...] wynikiem jest zakres nadrzędny (zakres oryginalny) i zakres podrzędny (zakres, który utworzyłeś).

Dokumentacja jest niepoprawna w tym zakresie, jak to pisze (a GitHub problem został złożony):

  • Relacje przodków (rodzic-dziecko) między zakresami istnieją tylko w kontekście danej sesji (obszar działania) .

    • Oznacza to, że dynamiczne określanie zakresów - widoczność zmiennych i innych definicji z zakresów przodków - ma zastosowanie tylko do zakresów w ramach danej sesji.

    • Godnym uwagi wyjątkiem jest to, że funkcje z modułu ma nie działać w zakresie dziecięcej zakresu dzwoni - chyba że to wywołanie zakresu dzieje się globalny zakres; moduły mają własne domeny zakresu (technicznie nazywane stanami sesji ), które są połączone tylko z zakresem globalnym - zapoznaj się z tym wydaniem dokumentacji GitHub w celu omówienia.

  • Dlatego żaden zakres podrzędny zakresu wywołującego nie jest tworzony w następujących scenariuszach, w których nowo uruchomiony kod nie wie nic o zmiennych (ani innych definicjach) w zakresie wywoływania :

    • Rozpoczynanie nowej sesji za pośrednictwem komunikacji zdalnej programu PowerShell (np. Za pomocą Enter-PSSession) lubInvoke-Command -Computer

    • Uruchamianie zadania w tle [wątku] z Start-Joblub Start-ThreadJoblub uruchamianie wątków równolegle z ForEach-Object -Parallelwersją 7.0 +

    • Uruchamianie nowego wystąpienia programu PowerShell (procesu) przy użyciu interfejsu wiersza polecenia programu PowerShell ( pwshdla programu PowerShell [Core], powershell.exedla programu Windows PowerShell).

    • Aby przekazać wartości z zakresu wywołującego do nowo uruchomionego kodu w tych scenariuszach, wymagana jest jawna akcja :

      • Podczas wywoływania CLI lub używania Start-Job, gdy tworzony jest proces potomny na tej samej maszynie, tylko zmienne środowiskowe zdefiniowane w procesie wywołującym stają się automatycznie dostępne dla procesu potomnego.
      • W przeciwnym razie wartości z obiektu wywołującego muszą zostać przekazane jako argumenty lub - z wyjątkiem sytuacji, gdy używany jest interfejs CLI - za pośrednictwem $using:zakresu - zobacz tę odpowiedź .
4 iRon Aug 24 2020 at 14:48

Zaczynając od tego drugiego pytania:

Zakresy wchodzą w grę z funkcjami i wywołanymi skryptami (cmdletami), takimi jak:

Function Test {
    $Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test

Zwroty:

Local: 6
Global: 5

I:

Function Test {
    $Global:Test++ Write-Host 'Local:' $Test
}
$Test = 5 Test Write-Host 'Global:' $Test

Zwroty:

Local: 6
Global: 6

Lub jeśli umieścisz funkcję w skrypcie (np. MyScript.ps1):

$Test = 5 .\MyScript.ps1 Write-Host $Test # $Test is unaffected unless you use the $Global scope in your script

Co zwróci w zasadzie te same wyniki, co powyżej, chyba że dot-Source swój skrypt, w którym będzie działał w bieżącym zakresie:

$Test = 5 . .\MyScript.ps1 Write-Host $Test # $Test might be affected by MyScript.ps1 if you just use $Test

Za to, co robisz:
tworzysz kompletną nową sesję PowerShell (z Powershell.exe), która rozpocznie się od nowej listy zmiennych.
Zauważ tutaj, że zobaczysz początkowe zmienne ponownie, jeśli jesteś exitz nowej sesji:

PS C:\> $Name = "John" PS C:\> Powershell.exe Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. Try the new cross-platform PowerShell https://aka.ms/pscore6 PS C:\> Write-Host 'New session' $Name
New session
PS C:\> Exit
PS C:\> Write-Host 'Initial session' $Name
Initial session John

Odnosząc się do pierwszego pytania, nie sądzę, że istnieje wiele aplikacji, w których trzeba wyraźnie odnieść się do $Localzakresu, ale aby podać przykład, w którym można go użyć:

$Test = 5 Function Test { Write-Host ($Local:Test++)
}
Test

W powyższym przykładzie jednoargumentowy operator inkrementacji rozpocznie się od, 0jeśli jawnie użyjesz $Localzakresu (w rzeczywistości zaczynasz od pustej zmiennej lokalnej, do której będzie rzutowany 0) i 5jeśli pominiesz $Localzakres, w którym odziedziczysz kopię $Testzmiennej z zakres nadrzędny.