o zakresach w programie Powershell
Dowiaduję się o zakresach w Powershell i mam kilka pytań:
- 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? - 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
Aby uzupełnić pomocną odpowiedź iRon :
- [...] 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$foow 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ązujeSet-StrictMode -Version 2lub wyższy, wystąpi błąd kończący instrukcję .
- 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ź .
- Podczas wywoływania CLI lub używania
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.