Kapitel 1. Die interaktive PowerShell-Shell
Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com
1.0 Einleitung
Das Design der PowerShell legt den Schwerpunkt auf die Verwendung als effiziente und leistungsstarke interaktive Shell. Sogar die Skriptsprache spielt dabei eine wichtige Rolle, da auch sie die interaktive Nutzung stark begünstigt.
Was die meisten Leute überrascht, wenn sie die PowerShell zum ersten Mal starten, ist ihre Ähnlichkeit mit der Eingabeaufforderung, die schon lange Teil von Windows ist. Vertraute Tools werden weiterhin ausgeführt. Vertraute Befehle werden weiterhin ausgeführt. Sogar die vertrauten Hotkeys sind dieselben. Hinter dieser vertrauten Benutzeroberfläche verbirgt sich jedoch eine leistungsstarke Engine, mit der du einst mühsame Verwaltungs- und Skriptaufgaben mit Leichtigkeit erledigen kannst.
Dieses Kapitel stellt die PowerShell aus der Perspektive ihrer interaktiven Shell vor.
1.1 PowerShell Core installieren
Lösung
Auf der Microsoft-Website findest du die Installationsanweisungen für das Betriebssystem und die Plattform, auf der du die Software installieren möchtest. Für die gängigsten:
Windows
Installiere PowerShell von Microsoft über die Microsoft Store-Anwendung im Startmenü. Installiere dann Windows Terminal von Microsoft über die Microsoft Store-Anwendung im Startmenü.
Mac
Installiere PowerShell aus Homebrew:
brew install --cask powershell
Linux
Die Installationsanweisungen variieren von Linux-Distribution zu Linux-Distribution, aber die häufigste Distribution unter PowerShell Core-Nutzern ist Ubuntu:
# Update the list of packages sudo apt-get update # Install pre-requisite packages. sudo apt-get install -y wget apt-transport-https software-properties-common # Download the Microsoft repository GPG keys wget -q https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb # Register the Microsoft repository GPG keys sudo dpkg -i packages-microsoft-prod.deb # Update the list of packages after we added packages.microsoft.com sudo apt-get update # Install PowerShell sudo apt-get install -y powershell
Diskussion
PowerShell hat bereits ein langes und aufregendes Leben hinter sich. In den ersten 15 Jahren ihres Bestehens war sie als "Windows PowerShell" bekannt: eine fantastische objektbasierte Verwaltungsshell und Plattform, die es Administratoren, Entwicklern und Power-Usern leicht machte, ihre Aufgaben zu erledigen.
In der Anfangsphase war diese Unterstützung Teil des "Windows Management Framework": ein eigenständiger Download, der diese dringend benötigte Funktionalität für Windows bereitstellte. Die Windows PowerShell wurde schließlich ein Teil von Windows selbst und ist seit Windows 7 ein Kernbestandteil des Betriebssystems.
2016 hat die PowerShell einen gewaltigen Wandel vollzogen, als sie ankündigte,die PowerShell auf mehreren Betriebssystemplattformen auszuliefern - und ganz nebenbei das gesamte Projekt gleichzeitig Open Source zu machen! Mit der neuen Zukunft bekam die Windows PowerShell einen neuen Namen: einfach PowerShell. Diese wichtige Änderung öffnete die Türen für wesentlich schnellere Innovationen, die Beteiligung der Community und die Verfügbarkeit auf Wegen, die vorher nicht möglich gewesen wären. Die klassische Windows PowerShell ist zwar immer noch standardmäßig im Betriebssystem enthalten, aber sie wird nicht mehr aktualisiert und sollte daher gemieden werden.
Installieren und Ausführen der PowerShell unter Windows
Wie in der Lösung erwähnt, ist der beste Weg, PowerShell über den Microsoft Store zu installieren. So ist sie einfach zu installieren und zu aktualisieren. Nach der Installation findest du PowerShell wie jede andereAnwendung im Startmenü.
Hinweis
Wenn du eine systemweite Version der PowerShell für die Automatisierung und andere Verwaltungsaufgaben installieren möchtest, wirst du wahrscheinlich den MSI-basierten Installationsmechanismus bevorzugen. Weitere Informationen findest du auf der Microsoft-Website.
Während du die PowerShell aus dem Microsoft Store installierst, ist jetzt ein guter Zeitpunkt, um die Windows Terminal-Anwendung aus dem Microsoft Store zu installieren. Die traditionelle Konsolenoberfläche (das Fenster, in dem die PowerShell läuft), die in Windows enthalten ist, hat so viele Tools und Anwendungen, die von ihren genauen Eigenheiten abhängen, dass es fast unmöglich ist, sie sinnvoll zu ändern. Deshalb ist die Windows Terminal-Anwendung aus dem Microsoft Store, wie in Abbildung 1-1 gezeigt, die Lösung. Wie auch die PowerShell ist sie Open Source, ein Fokus auf schnelle Innovation und eine enorme Verbesserung gegenüber dem, was standardmäßig in Windows enthalten ist.
Du kannst viele Shells innerhalb von Tabs in Windows Terminal ausführen: PowerShell, Windows PowerShell, cmd.exe, Bash (wenn du das Windows Subsystem für Linux aktiviert hast) und sogar eine Verbindung zu Azure Cloud Shell. Windows Terminal verwendet standardmäßig die PowerShell, wenn du sie installiert hast.
PowerShell auf dem Windows Terminal anpassen
Es gibt zwei Änderungen an der Standardinstallation von Windows Terminal + PowerShell, die das Erlebnis wirklich verbessern: die Anheftung an die Taskleiste und die Themen.
Anheften der Taskleiste
Wenn du Windows Terminal startest, klicke mit der rechten Maustaste auf das Symbol in der Taskleiste. Wähle "An die Taskleiste anheften" und ziehe das Symbol dann ganz nach links in die Taskleiste. Wenn du von nun an die Windows-Taste und 1
gleichzeitig drückst, werden Windows Terminal und PowerShell entweder gestartet (wenn sie nicht bereits geöffnet sind) oder aktiviert. Auf diese Weise hast du deine Lieblingsshell immer griffbereit.
Themen
Windows PowerShell hat ein wunderschönes Noble Blue Theme. Es ist schön anzusehen, schnell zu erkennen und hebt sich von den Dutzenden anderer Shells ab. PowerShell Core hat dieses Farbschema nicht standardmäßig mitgenommen, aber es ist trotzdem möglich, deine Installation anzupassen. Drücke im Windows Terminal die Tastenkombination Strg+Komma oder klicke auf den Abwärtspfeil auf der rechten Seite des Tabstrips, um den Einstellungsdialog des Windows Terminals zu öffnen. Die Datei, die diese Einstellungen enthält, wird in deinem Standardtexteditor geöffnet. Suche unter Profiles
das Element mit Windows.Terminal.PowershellCore
als Quelle und füge Campbell Powershell
als colorScheme
hinzu. Das Ergebnis sollte so aussehen:
{ "guid": ... "hidden": false, "name": "PowerShell", "colorScheme": "Campbell Powershell", "source": "Windows.Terminal.PowershellCore" },
Achte auf Groß- und Kleinschreibung, Anführungszeichen, Doppelpunkte und Kommas, und deine PowerShell-Sitzungen sollten im Handumdrehen wieder edel aussehen!
Installieren und Ausführen der PowerShell auf Mac und Linux
Im Großen und Ganzen folgt die Installation der PowerShell auf Mac und Linux den Mustern, mit denen du wahrscheinlich schon vertraut bist.
Auf dem Mac ist die empfohlene Installationsmethode der beliebte Homebrew-Paketmanager. Homebrew ist unter macOS nicht standardmäßig installiert, aber die Installation ist ganz einfach. Wenn du Homebrew noch nicht installiert hast, findest du eine Anleitung auf der offiziellen Homebrew-Seite.
Unter Linux variieren die Installationsmethoden je nach Distribution, an der du interessiert bist. In den meisten Fällen ist die Installation so einfach wie das Registrieren des Microsoft-Repositorys für den Paketmanager deiner Distribution und die anschließende Installation der PowerShell. Die Lösung enthält ein Beispiel für Ubuntu 20.04
, aber auf der Microsoft-Website findest du spezifische Anweisungen für deine Distribution und deine Version.
1.2 Programme, Skripte und vorhandene Tools ausführen
Problem
Du verlässt dich auf eine Menge Arbeit, die du in deine aktuellen Tools investiert hast. Du hast herkömmliche ausführbare Dateien, Perl-Skripte, VBScript und natürlich ein Legacy-Build-System, das organisch zu einem Wirrwarr von Batch-Dateien gewachsen ist. Du willst die PowerShell nutzen, aber du willst nicht alles aufgeben, was du bereits hast.
Lösung
Um ein Programm, Skript, eine Batch-Datei oder einen anderen ausführbaren Befehl im Systempfad auszuführen, gibst du den Dateinamen ein. Bei diesen ausführbaren Dateien ist die Erweiterung optional:
Program
.
exe
arguments
ScriptName
.
ps1
arguments
BatchFile
.
cmd
arguments
Um einen Befehl auszuführen, der ein Leerzeichen in seinem Namen enthält, schließe den Dateinamen in einfache Anführungszeichen ein ('
) und stelle dem Befehl ein kaufmännisches Und (&
) voran, was in der PowerShell als Invoke-Operator bekannt ist:
&
'C:\Program Files\Program\Program.exe'
arguments
Um einen Befehl im aktuellen Verzeichnis auszuführen, stellst du .\
vor den Dateinamen:
.\
Program
.
exe
arguments
Um einen Befehl mit Leerzeichen in seinem Namen aus dem aktuellen Verzeichnis heraus auszuführen, stellst du ihm ein kaufmännisches Und und .\
voran:
&
'.\Program With Spaces.exe'
arguments
Diskussion
In diesem Fall besteht die Lösung hauptsächlich darin, dass du deine aktuellen Tools wie bisher verwendest. Der einzige Unterschied ist, dass du sie in der interaktiven PowerShell und nicht in cmd.exe ausführst.
Angeben des Befehlsnamens
Die letzten drei Tipps in der Lösung verdienen besondere Aufmerksamkeit. Es handelt sich dabei um die Funktionen der PowerShell, über die viele neue Benutzer stolpern, wenn es um das Ausführen von Programmen geht. Der erste betrifft die Ausführung von Befehlen, die Leerzeichen enthalten. In cmd.exe wird ein Befehl mit Leerzeichen ausgeführt, indem er in Anführungszeichen gesetzt wird:
"C:\Program Files\Program\Program.exe"
In der PowerShell ist das Setzen von Text in Anführungszeichen jedoch Teil einer Funktion, mit der du komplexe Ausdrücke an der Eingabeaufforderung auswerten kannst, wie in Beispiel 1-1 gezeigt.
Beispiel 1-1. Ausdrücke an der Eingabeaufforderung der PowerShell auswerten
PS > 1 + 1 2 PS > 26 * 1.15 29.9 PS > "Hello" + " World" Hello World PS > "Hello World" Hello World PS > "C:\Program Files\Program\Program.exe" C:\Program Files\Program\Program.exe PS >
Ein Programmname in Anführungszeichen unterscheidet sich also nicht von jeder anderen Zeichenkette in Anführungszeichen. Er ist einfach ein Ausdruck. Wie bereits gezeigt, kannst du einen Befehl in einer Zeichenkette ausführen, indem du dieser Zeichenkette den Operator invoke (&
) voranstellst. Wenn es sich bei dem auszuführenden Befehl um eine Batch-Datei handelt, die ihre Umgebung verändert, siehe Rezept 3.5.
Hinweis
Standardmäßig verhindern die Sicherheitsrichtlinien der PowerShell, dass Skripte ausgeführt werden. Sobald du jedoch anfängst, Skripte zu schreiben oder zu verwenden, solltest du diese Richtlinie so konfigurieren, dass sie weniger restriktiv ist. Informationen darüber, wie du deine Ausführungsrichtlinie konfigurierst, findest du in Rezept 18.1.
Der zweite Befehl, über den neue Benutzer (und erfahrene Veteranen vor dem Kaffee!) manchmal stolpern, ist das Ausführen von Befehlen aus dem aktuellen Verzeichnis. In cmd.exe wird das aktuelle Verzeichnis als Teil des Pfades betrachtet: die Liste der Verzeichnisse, die Windows durchsucht, um den von dir eingegebenen Programmnamen zu finden. Wenn du dich im Verzeichnis C:\Programme befindest, sucht cmd.exe (unter anderem) in C:\Programme nach Anwendungen, die ausgeführt werden sollen.
Wie die meisten Unix-Shells verlangt auch die PowerShell, dass du explizit angibst, dass du ein Programm aus dem aktuellen Verzeichnis ausführen möchtest. Dazu verwendest du die .\Program.exe
Syntax, wie oben gezeigt. Dadurch wird verhindert, dass böswillige Benutzer deine Festplatte mit bösen Programmen übersäen, die ähnliche (oder gleiche) Namen wie die Befehle haben, die du beim Besuch des Verzeichnisses ausführen könntest.
Um sich die Eingabe des Speicherorts häufig verwendeter Skripte und Programme zu ersparen, legen viele Benutzer häufig verwendete Dienstprogramme zusammen mit ihren PowerShell-Skripten in einem "Tools"-Verzeichnis ab, das sie dem Pfad ihres Systems hinzufügen. Wenn PowerShell ein Skript oder Dienstprogramm im Systempfad finden kann, musst du seinen Speicherort nicht explizit angeben.
Wenn du möchtest, dass PowerShell automatisch in deinem aktuellen Arbeitsverzeichnis nach Skripten sucht, kannst du einen Punkt (.
) an deine PATH-Umgebungsvariable anhängen.
Weitere Informationen zum Aktualisieren deines Systempfads findest du in Rezept 16.2.
Wenn du die Ausgabe eines Befehls festhalten willst, kannst du die Ergebnisse entweder in einer Variablen oder in einer Datei speichern. Um die Ergebnisse in einer Variablen zu speichern, siehe Rezept 3.3. Um die Ergebnisse in einer Datei zu speichern, siehe Rezept 9.2.
Angeben von Befehlsargumenten
Um Argumente für einen Befehl anzugeben, kannst du sie genau wie in anderen Shells eingeben. Um zum Beispiel eine bestimmte Datei schreibgeschützt zu machen (zwei Argumente für attrib.exe), gibst du einfach ein:
attrib
+
R
c
:
\
path
\
to
\
file
.
txt
Wenn es um Befehlsargumente geht, werden viele Skripter in die Irre geführt, wenn es darum geht, wie du sie in deinen Skripten ändern kannst. Wie bekommst du zum Beispiel den Dateinamen aus einer PowerShell-Variablen? Die Antwort ist, eine Variable zu definieren, die den Wert des Arguments enthält, und diese an der Stelle zu verwenden, an der du das Befehlsargument geschrieben hast:
$filename
=
"c:\path\to\other\file.txt"
attrib
+
R
$filename
Du kannst die gleiche Technik verwenden, wenn du ein PowerShell-Cmdlet, -Skript oder eineFunktion aufrufst:
$filename
=
"c:\path\to\other\file.txt"
Get-Acl
-Path
$filename
Wenn du unter eine Lösung siehst, die das Cmdlet Invoke-Expression
zum Zusammenstellen von Befehlsargumenten verwendet, ist sie mit großer Wahrscheinlichkeit falsch. Das Cmdlet Invoke-Expression
nimmt die Zeichenfolge, die du ihm gibst, und behandelt sie wie ein vollständiges PowerShell-Skript. ist nur ein Beispiel für die Probleme, die dies verursachen kann: Dateinamen dürfen das Zeichen Semikolon (;
) enthalten, aber wenn Invoke-Expression
ein Semikolon sieht, nimmt es an, dass es sich um eine neue Zeile eines PowerShell-Skripts handelt. Probiere zum Beispiel Folgendes aus:
$filename
=
"c:\file.txt; Write-Warning 'This could be bad'"
Invoke-Expression
"Get-Acl -Path $filename"
Da diese dynamischen Argumente oft aus Benutzereingaben stammen, kann die Verwendung von Invoke-Expression
zum Zusammenstellen von Befehlen (im besten Fall) zu unvorhersehbaren Skriptergebnissen führen. Schlimmer noch, es könnte zu Schäden an deinem System oder zu einer Sicherheitslücke führen.
In der PowerShell kannst du nicht nur einzelne Argumente in Form von Variablen übergeben, sondern auch mehrere auf einmal, und zwar mit einer Technik, die als Splatting bekannt ist. Weitere Informationen zum Splatting findest du in Rezept 11.14.
1.3 Einen PowerShell-Befehl ausführen
Lösung
Um einen PowerShell-Befehl auszuführen, gibst du den Namen des Befehls in die Eingabeaufforderung ein. Zum Beispiel:
PS > Get-Process NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName ------ ----- ----- ------ -- -- ----------- 14 3.47 10.55 0.00 6476 0 AGMService 14 3.16 10.57 0.00 3704 0 AGSService 37 40.12 40.51 2.06 17676 1 ApplicationFrameHost
Diskussion
Der Befehl Get-Process
ist ein Beispiel für einen nativen PowerShell-Befehl, ein sogenanntes Cmdlet. Im Vergleich zu herkömmlichen Befehlen bieten Cmdlets sowohl für Administratoren als auch für Entwickler erhebliche Vorteile:
-
Sie haben eine gemeinsame und regelmäßige Kommandozeilen-Syntax.
-
Sie unterstützen umfangreiche Pipeline-Szenarien (Verwendung der Ausgabe eines Befehls als Eingabe für einen anderen).
-
Sie erzeugen eine einfach zu handhabende objektbasierte Ausgabe anstelle einer fehleranfälligen Klartextausgabe.
Da das Cmdlet Get-Process
eine umfangreiche objektbasierte Ausgabe erzeugt, kannst du die Ausgabe für viele prozessbezogene Aufgaben verwenden.
Bei jedem PowerShell-Befehl kannst du über die Parameter Eingaben für den Befehl machen. Weitere Informationen zur Eingabe von Befehlen findest du unter "Befehle ausführen".
Das Cmdlet Get-Process
ist nur einer von vielen, die PowerShell unterstützt. In Rezept 1.12 erfährst du, wie du weitere von der PowerShell unterstützte Befehle finden kannst.
Weitere Informationen zur Arbeit mit Klassen aus dem .NET Framework findest du in Rezept 3.8.
1.4 Fehler beim Aufrufen nativer Ausführungsprogramme beheben
Lösung
Schließe alle betroffenen Befehlsargumente in einfache Anführungszeichen ein, um zu verhindern, dass sie von der PowerShell interpretiert werden, und ersetze alle einfachen Anführungszeichen im Befehl durch zwei einfache Anführungszeichen:
PS > cmd /c echo '!"#$%&''()*+,-./09:;<=>?@AZ[\]^_`az{|}~' !"#$%&'()*+,-./09:;<=>?@AZ[\]^_`az{|}~
Für komplizierte Befehle, bei denen das nicht funktioniert, verwende die Syntax für wörtliche Argumente(--%
):
PS > cmd /c echo 'quotes' "and" $variables @{ etc = $true } quotes and System.Collections.Hashtable PS > cmd --% /c echo 'quotes' "and" $variables @{ etc = $true } 'quotes' "and" $variables @{ etc = $true }
Diskussion
Eines der wichtigsten Ziele der PowerShell war schon immer die Konsistenz der Befehle. Aus diesem Grund sind Cmdlets sehr regelmäßig in der Art und Weise, wie sie Parameter akzeptieren. Native Cmdlets schreiben ihr eigenes Parameter-Parsing, sodass du nie weißt, was dich bei der Arbeit mit ihnen erwartet. Außerdem bietet die PowerShell viele Funktionen, die deine Arbeit in der Befehlszeile effizienter machen: Befehlssubstitution, Variablenexpansion und vieles mehr. Da viele native ausführbare Dateien vor der Entwicklung der PowerShell geschrieben wurden, verwenden sie möglicherweise Sonderzeichen, die mit diesen Funktionen in Konflikt stehen.
Als Beispiel verwendet der Befehl in der Lösung alle Sonderzeichen, die auf einer typischen Tastatur verfügbar sind. Ohne die Anführungszeichen behandelt PowerShell einige von ihnen als Sprachmerkmale, wie in Tabelle 1-1 gezeigt.
Sonderzeichen | Bedeutung |
---|---|
|
Der Anfang (oder das Ende) des zitierten Textes |
|
Der Anfang eines Kommentars |
|
Der Anfang einer Variable |
|
Der Pipeline-Betreiber im Hintergrund |
|
Klammern werden für Unterausdrücke verwendet |
|
Statement Separator |
|
Skript-Block |
|
Rohrleitungsabscheider |
|
Escape-Zeichen |
Wenn sie von einfachen Anführungszeichen umgeben sind, akzeptiert PowerShell diese Zeichen wie geschrieben, ohne die besondere Bedeutung.
Trotz dieser Vorsichtsmaßnahmen kann es vorkommen, dass du auf einen Befehl stößt, der nicht funktioniert, wenn er von der PowerShell aufgerufen wird. In den meisten Fällen kannst du diese Probleme mit dem Verbatim-Argument-Marker (--%
) lösen, der verhindert, dass PowerShell die restlichen Zeichen in der Zeile interpretiert. Du kannst diese Markierung an einer beliebigen Stelle in den Befehlsargumenten platzieren, damit du die PowerShell-Konstrukte nutzen kannst, wo es sinnvoll ist. Im folgenden Beispiel wird für einige der Befehlsargumente eine PowerShell-Variable verwendet, für den Rest jedoch die wörtlichen Argumente:
PS > $username = "Lee" PS > cmd /c echo Hello $username with 'quotes' "and" $variables @{ etc = $true } Hello Lee with quotes and System.Collections.Hashtable PS > cmd /c echo Hello $username ` --% with 'quotes' "and" $variables @{ etc = $true } Hello Lee with 'quotes' "and" $variables @{ etc = $true }
In diesem Modus akzeptiert die PowerShell auch Umgebungsvariablen im Stil von cmd.exe, da diese häufig in Befehlen verwendet werden, die "früher einfach funktioniert haben":
PS > $env:host = "localhost" PS > ping %host% Ping request could not find host %host%. Please check the name and try again. PS > ping --% %host% Pinging localhost [127.0.1.1] with 32 bytes of data: (...)
Siehe auch
1.5 Standardwerte für Parameter bereitstellen
Lösung
Füge einen Eintrag in die Hashtabelle PSDefaultParameterValues
hinzu:
PS > Get-Process Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 150 13 9692 9612 39 21.43 996 audiodg 1013 84 45572 42716 315 1.67 4596 WWAHost (...) PS > $PSDefaultParameterValues["Get-Process:ID"] = $pid PS > Get-Process Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 584 62 132776 157940 985 13.15 9104 powershell PS > Get-Process -Id 0 Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName ------- ------ ----- ----- ----- ------ -- ----------- 0 0 0 20 0 0 Idle
Diskussion
In der PowerShell haben viele Befehle (Cmdlets und erweiterte Funktionen) Parameter, mit denen du ihr Verhalten konfigurieren kannst. Eine vollständige Beschreibung der Eingabe von Befehlen findest du unter "Befehle ausführen". Manchmal ist es jedoch umständlich oder wiederholend, die Werte für diese Parameter bei jedem Aufruf einzugeben.
In frühen Versionen der PowerShell war es die Aufgabe der Cmdlet-Autoren, umständliche oder sich wiederholende Konfigurationseigenschaften zu erkennen und Unterstützung für "Einstellungsvariablen" in das Cmdlet selbst einzubauen. Das Cmdlet Send-MailMessage
sucht zum Beispiel nach der Variable $PSEmailServer
, wenn du keinen Wert für seinen Parameter angibst.-SmtpServer
Parameter angibst.
Um diese Unterstützung einheitlicher und konfigurierbar zu machen, unterstützt die PowerShell die Einstellungsvariable PSDefaultParameterValues
. Diese Einstellungsvariable ist eine Hashtabelle. Wie alle anderen PowerShell-Hashtables bestehen die Einträge aus zwei Teilen: dem Schlüssel und dem Wert.
Die Schlüssel in der Hashtabelle PSDefaultParameterValues
müssen dem Muster cmdlet
:parameter
-das heißt, ein Cmdlet-Name und ein Parametername, getrennt durch einen Doppelpunkt. Für jeden (oder beide) können Platzhalter verwendet werden, und Leerzeichen zwischen Befehlsname, Doppelpunkt und Parameter werden ignoriert.
Die Werte für die cmdlet/Parameterpaare können entweder ein einfacher Parameterwert (eine Zeichenkette, ein boolescher Wert, eine ganze Zahl usw.) oder ein Skriptblock sein. Die einfachen Parameterwerte wirst du am häufigsten verwenden.
Wenn du möchtest, dass sich der Standardwert dynamisch ändert, je nachdem, welche Parameterwerte bisher angegeben wurden, kannst du einen Skriptblock als Standardwert verwenden. Wenn du das tust, wertet die PowerShell den Skriptblock aus und verwendet sein Ergebnis als Standardwert. Wenn dein Skriptblock kein Ergebnis zurückgibt, wendet PowerShell keinen Standardwert an.
Wenn PowerShell deinen Skriptblock aufruft, enthält $args[0]
Informationen über alle bisher gebundenen Parameter: BoundDefaultParameters
, BoundParameters
, und BoundPositionalParameters
. Ein Beispiel hierfür ist die Angabe von Standardwerten für den Parameter -Credential
auf der Grundlage des Computers, mit dem eine Verbindung besteht. Hier ist eine Funktion, die einfach die verwendeten Anmeldeinformationen ausgibt:
function
RemoteConnector
{
param
(
[
Parameter
()]
$ComputerName
,
[
Parameter
(
Mandatory
=
$true
)]
$Credential
)
"Connecting as "
+
$Credential
.
UserName
}
Jetzt kannst du eine Credential Map definieren:
PS > $credmap = @{} PS > $credmap["RemoteComputer1"] = Get-Credential PS > $credmap["RemoteComputer2"] = Get-Credential
Erstelle dann eine Parametervorgabe für alle Credential
Parameter, die sich auf denComputerName
gebundenen Parameter:
$PSDefaultParameterValues
[
"*:Credential"
]
=
{
if
(
$args
[
0
].
BoundParameters
-contains
"ComputerName"
)
{
$cred
=
$credmap
[
$PSBoundParameters
[
"ComputerName"
]]
if
(
$cred
)
{
$cred
}
}
}
Hier ist ein Beispiel dafür, wie das funktioniert:
PS > RemoteConnector -ComputerName RemoteComputer1 Connecting as UserForRemoteComputer1 PS > RemoteConnector -ComputerName RemoteComputer2 Connecting as UserForRemoteComputer2 PS > RemoteConnector -ComputerName RemoteComputer3 cmdlet RemoteConnector at command pipeline position 1 Supply values for the following parameters: Credential: (...)
Weitere Informationen zur Arbeit mit Hashtables in der PowerShell findest du unter "Hashtables (Assoziative Arrays)".
1.6 Aufrufen eines lang laufenden oder im Hintergrund laufenden Befehls
Lösung
Invoke den Befehl als Job
, damit die PowerShell ihn im Hintergrund ausführt:
PS > Start-Job { while($true) { Get-Random; Start-Sleep 5 } } -Name Sleeper Id Name State HasMoreData Location -- ---- ----- ----------- -------- 1 Sleeper Running True localhost PS > Receive-Job Sleeper 671032665 1862308704 PS > Stop-Job Sleeper
Oder, wenn Ihr Befehl eine einzelne Pipeline ist, setzen Sie ein &
Zeichen am Ende der Zeile ein, um diese Pipeline im Hintergrund auszuführen:
PS > dir c:\windows\system32 -recurse & Id Name PSJobTypeName State HasMore Data -- ---- ------------- ----- ------- 1 Job1 BackgroundJob Running True PS > 1+1 2 PS > Receive-Job -id 1 | Select -First 5 Directory: C:\Windows\System32 Mode LastWriteTime Length Name ---- ------------- ------ ---- d---- 12/7/2019 1:50 AM 0409 d---- 11/5/2020 7:09 AM 1028 d---- 11/5/2020 7:09 AM 1029 d---- 11/5/2020 7:09 AM 1031 d---- 11/5/2020 7:09 AM 1033
Diskussion
Die Auftrags-Cmdlets der PowerShell bieten eine einheitliche Methode zur Erstellung und Interaktion mit Hintergrundaufgaben. In der Lösung verwenden wir das Cmdlet Start-Job
, um einen Hintergrundauftrag auf dem lokalen Computer zu starten. Wir geben ihm den Namen Sleeper
, aber ansonsten passen wir die Ausführungsumgebung kaum an.
Unter kannst du nicht nur den Auftragsnamen anpassen, sondern mit dem Cmdlet Start-Job
auch den Auftrag unter anderen Benutzeranmeldeinformationen oder als 32-Bit-Prozess starten (wenn er ursprünglich von einem 64-Bit-Prozess ausgeführt wurde).
Als Alternative zum cmdlet Start-Job
kannst du auch das cmdlet Start-ThreadJob
verwenden. Das Cmdlet Start-ThreadJob
ist etwas schneller beim Starten von Hintergrundaufträgen und ermöglicht es dir außerdem, Live-Objekte in den von dir erstellten Aufträgen bereitzustellen und mit ihnen zu interagieren. Es verbraucht jedoch Ressourcen deines aktuellen PowerShell-Prozesses und ermöglicht es dir nicht, deinen Auftrag unter alternativen Benutzeranmeldeinformationen auszuführen.
Sobald du einen Auftrag gestartet hast, kannst du die anderen Cmdlets von Job
verwenden, um mit ihm zu interagieren:
Get-Job
-
Ruft alle Aufträge ab, die mit der aktuellen Sitzung verbunden sind. Außerdem kannst du mit den Parametern
-Before
,-After
,-Newest
und-State
kannst du die Aufträge nach ihrem Status oder ihrer Abschlusszeit filtern. Wait-Job
-
Wartet auf auf einen Auftrag, bis die Ausgabe zum Abruf bereit ist.
Receive-Job
-
Ruft alle Ausgaben ab, die der Auftrag seit dem letzten Aufruf von
Receive-Job
erzeugt hat. Stop-Job
Remove-Job
Hinweis
In kannst du neben dem Cmdlet Start-Job
auch den Parameter -AsJob
in vielen Cmdlets verwenden, damit diese ihre Aufgaben im Hintergrund ausführen. Zwei der nützlichsten Beispiele sind das Cmdlet Invoke-Command
(für den Einsatz auf entfernten Computern) und das Cmdlet ForEach-Object
.
Wenn dein Auftrag einen Fehler erzeugt, zeigt dir das Cmdlet Receive-Job
diesen an, wenn du die Ergebnisse erhältst, wie in Beispiel 1-2 gezeigt. Wenn du diese Fehler genauer untersuchen möchtest, zeigt das von Get-Job
zurückgegebene Objekt sie über die Eigenschaft Error
an.
Beispiel 1-2. Abrufen von Fehlern aus einem Auftrag
PS > Start-Job -Name ErrorJob { Write-Error Error! } Id Name State HasMoreData Location -- ---- ----- ----------- -------- 1 ErrorJob Running True localhost PS > Receive-Job ErrorJob Write-Error: Error! PS > $job = Get-Job ErrorJob PS > $job | Format-List * State : Completed HasMoreData : False StatusMessage : Location : localhost Command : Write-Error Error! JobStateInfo : Completed Finished : System.Threading.ManualResetEvent InstanceId : 801e932c-5580-4c8b-af06-ddd1024840b7 Id : 1 Name : ErrorJob ChildJobs : {Job2} Output : {} Error : {} Progress : {} Verbose : {} Debug : {} Warning : {} PS > $job.ChildJobs[0] | Format-List * State : Completed StatusMessage : HasMoreData : False Location : localhost Runspace : System.Management.Automation.RemoteRunspace Command : Write-Error Error! JobStateInfo : Completed Finished : System.Threading.ManualResetEvent InstanceId : 60fa85da-448b-49ff-8116-6eae6c3f5006 Id : 2 Name : Job2 ChildJobs : {} Output : {} Error : {Microsoft.PowerShell.Commands.WriteErrorException,Microso ft.PowerShell.Commands.WriteErrorCommand} Progress : {} Verbose : {} Debug : {} Warning : {} PS > $job.ChildJobs[0].Error Write-Error: Error! PS >
Wie dieses Beispiel zeigt, sind Aufträge manchmal Container für andere Aufträge, so genannte Unteraufträge. Aufträge, die mit dem Cmdlet Start-Job
erstellt werden, sind immer Unteraufträge, die an einen generischen Container angehängt sind. Um auf die Fehler zuzugreifen, die von diesen Aufträgen zurückgegeben werden, greifst du stattdessen auf die Fehler im ersten Unterauftrag zu (Unterauftrag Nummer Null).
Neben lang laufenden Aufträgen, die unter der Kontrolle der aktuellen PowerShell-Sitzung ausgeführt werden, möchtest du vielleicht auch Aufträge registrieren und steuern, die nach einem Zeitplan oder unabhängig von der aktuellen PowerShell-Sitzung ausgeführt werden. Die PowerShell verfügt über eine Handvoll Befehle, mit denen du mit solchen geplanten Aufträgen arbeiten kannst; weitere Informationen findest du in Rezept 27.14.
1.7 Programm: Einen Befehl auf Änderungen überwachen
So aufregend unser Leben auch sein mag, manche Tage bestehen darin, einen Befehl immer und immer wieder auszuführen. Wurden die Dateien schon fertig kopiert? Ist der Bau abgeschlossen? Ist die Website noch online?
Normalerweise erhältst du die Antwort auf diese Fragen, indem du einen Befehl ausführst, dir seine Ausgabe ansiehst und dann entscheidest, ob er deine Kriterien erfüllt. In der Regel bedeutet das, dass du einfach darauf wartest, dass sich die Ausgabe ändert, dass ein Text erscheint oder dass ein Text verschwindet.
Zum Glück automatisiert Beispiel 1-3 diesen mühsamen Prozess für dich.
Beispiel 1-3. Watch-Befehl.ps1
##############################################################################
##
## Watch-Command
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Watches the result of a command invocation, alerting you when the output
either matches a specified string, lacks a specified string, or has simply
changed.
.EXAMPLE
PS > Watch-Command { Get-Process -Name Notepad | Measure } -UntilChanged
Monitors Notepad processes until you start or stop one.
.EXAMPLE
PS > Watch-Command { Get-Process -Name Notepad | Measure } -Until "Count : 1"
Monitors Notepad processes until there is exactly one open.
.EXAMPLE
PS > Watch-Command {
Get-Process -Name Notepad | Measure } -While 'Count : \d\s*\n'
Monitors Notepad processes while there are between 0 and 9 open
(once number after the colon).
#>
[
CmdletBinding
(
DefaultParameterSetName
=
"Forever"
)]
param
(
## The script block to invoke while monitoring
[
Parameter
(
Mandatory
=
$true
,
Position
=
0
)]
[ScriptBlock]
$ScriptBlock
,
## The delay, in seconds, between monitoring attempts
[
Parameter
()]
[Double]
$DelaySeconds
=
1
,
## Specifies that the alert sound should not be played
[
Parameter
()]
[Switch]
$Quiet
,
## Monitoring continues only while the output of the
## command remains the same.
[
Parameter
(
ParameterSetName
=
"UntilChanged"
,
Mandatory
=
$false
)]
[Switch]
$UntilChanged
,
## The regular expression to search for. Monitoring continues
## until this expression is found.
[
Parameter
(
ParameterSetName
=
"Until"
,
Mandatory
=
$false
)]
[String]
$Until
,
## The regular expression to search for. Monitoring continues
## until this expression is not found.
[
Parameter
(
ParameterSetName
=
"While"
,
Mandatory
=
$false
)]
[String]
$While
)
Set-StrictMode
-Version
3
$initialOutput
=
""
$lastCursorTop
=
0
Clear-Host
## Start a continuous loop
while
(
$true
)
{
## Run the provided script block
$r
=
&
$ScriptBlock
## Clear the screen and display the results
$buffer
=
$ScriptBlock
.
ToString
().
Trim
()
+
"
`r`n
"
$buffer
+=
"
`r`n
"
$textOutput
=
$r
|
Out-String
$buffer
+=
$textOutput
[Console]
::
SetCursorPosition
(
0
,
0
)
[Console]
::
Write
(
$buffer
)
$currentCursorTop
=
[Console]
::
CursorTop
$linesToClear
=
$lastCursorTop
-
$currentCursorTop
if
(
$linesToClear
-gt
0
)
{
[Console]
::
Write
((
" "
*
[Console]
::
WindowWidth
*
$linesToClear
))
}
$lastCursorTop
=
[Console]
::
CursorTop
[Console]
::
SetCursorPosition
(
0
,
0
)
## Remember the initial output, if we haven't
## stored it yet
if
(
-not
$initialOutput
)
{
$initialOutput
=
$textOutput
}
## If we are just looking for any change,
## see if the text has changed.
if
(
$UntilChanged
)
{
if
(
$initialOutput
-ne
$textOutput
)
{
break
}
}
## If we need to ensure some text is found,
## break if we didn't find it.
if
(
$While
)
{
if
(
$textOutput
-notmatch
$While
)
{
break
}
}
## If we need to wait for some text to be found,
## break if we find it.
if
(
$Until
)
{
if
(
$textOutput
-match
$Until
)
{
break
}
}
## Delay
Start-Sleep
-Seconds
$DelaySeconds
}
## Notify the user
if
(
-not
$Quiet
)
{
[Console]
::
Beep
(
1000
,
1000
)
}
Weitere Informationen zum Ausführen von Skripten findest du in Rezept 1.2.
1.8 Benachrichtige dich selbst über die Fertigstellung des Auftrags
Lösung
Verwende den Befehl Register-TemporaryEvent
aus Rezept 31.3, um dich für das Ereignis StateChanged
zu registrieren:
PS > $job = Start-Job -Name TenSecondSleep { Start-Sleep 10 } PS > Register-TemporaryEvent $job StateChanged -Action { [Console]::Beep(100,100) Write-Host "Job #$($sender.Id) ($($sender.Name)) complete." } PS > Job #6 (TenSecondSleep) complete. PS >
Diskussion
Wenn ein Auftrag abgeschlossen ist, wird ein Ereignis StateChanged
ausgelöst, um die Abonnenten zu benachrichtigen, dass sich sein Status geändert hat. Wir können die PowerShell-Cmdlets zur Ereignisbehandlung verwenden, um uns für Benachrichtigungen über dieses Ereignis zu registrieren, aber sie sind nicht auf diese Art der einmaligen Ereignisbehandlung ausgerichtet. Um das Problem zu lösen, verwenden wir den Befehl Register-TemporaryEvent
aus Rezept 31.3.
In unserem Beispiel-Aktionsblock in der Lösung geben wir einfach einen Piepton aus und schreiben eine Nachricht, dass der Auftrag abgeschlossen ist.
Du kannst auch deine prompt
Funktion aktualisieren, um Aufträge zu markieren, die abgeschlossen sind, aber noch nicht verarbeitet wurden:
$psJobs
=
@(
Get-Job
-State
Completed
|
?
{
$_
.
HasMoreData
})
if
(
$psJobs
.
Count
-gt
0
)
{
(
$psJobs
|
Out-String
).
Trim
()
|
Write-Host
-Fore
Yellow
}
Weitere Informationen über Ereignisse und diese Art der automatischen Ereignisbehandlung findest du in Kapitel 31.
1.9 Anpassen von Shell, Profil und Eingabeaufforderung
Lösung
Wenn du Aspekte der PowerShell anpassen möchtest, platzierst du diese Anpassungen in deinem persönlichen Profilskript. Die PowerShell ermöglicht einen einfachen Zugriff auf dieses Profilskript, indem sie seinen Speicherort in der Variablen $profile
speichert.
Hinweis
Standardmäßig verhindern die Sicherheitsrichtlinien der PowerShell, dass Skripte (einschließlich deines Profils) ausgeführt werden. Sobald du jedoch anfängst, Skripte zu schreiben, solltest du diese Richtlinie so konfigurieren, dass sie weniger restriktiv ist. Informationen darüber, wie du deine Ausführungsrichtlinie konfigurierst, findest du in Rezept 18.1.
So erstellst du ein neues Profil (und überschreibst eines, wenn es bereits existiert):
New-Item
-type
file
-force
$profile
Um dein Profil zu bearbeiten (in Visual Studio Code, wenn du es installiert hast):
code
$profile
Um deine Profildatei zu sehen:
Get-ChildItem
$profile
Sobald du ein Profilskript erstellt hast, kannst du eine Funktion namens prompt
hinzufügen, die eine Zeichenkette zurückgibt. Die PowerShell zeigt die Ausgabe dieser Funktion als Eingabeaufforderung in der Befehlszeile an:
function
prompt
{
"PS [$env:COMPUTERNAME] >"
}
Diese Eingabeaufforderung zeigt den Namen deines Computers an und sieht aus wie PS [LEE-DESK] >
.
Vielleicht findest du es auch hilfreich, deinem Profil Aliase hinzuzufügen. Mit Aliasen kannst du häufige Befehle mit einem von dir gewählten Namen bezeichnen. Mit persönlichen Profilskripten kannst du automatisch Aliase, Funktionen, Variablen oder andere Anpassungen definieren, die du interaktiv über die Eingabeaufforderung der PowerShell festlegen kannst. Aliase gehören zu den gängigsten Anpassungen, da sie es dir ermöglichen, PowerShell-Befehle (und deine eigenen Skripte) mit einem Namen zu versehen, der sich leichter eingeben lässt.
Hinweis
Wenn du einen Alias für einen Befehl definieren willst, aber auch die Parameter dieses Befehls ändern musst, dann definiere stattdessen eine Funktion. Weitere Informationen findest du in Rezept 11.14.
Zum Beispiel:
Set-Alias
new
New-Object
Set-Alias
browse
'C:\Users\lee\AppData\Local\Microsoft\*\MicrosoftEdge.exe'
Deine Änderungen werden wirksam, sobald du dein Profil speicherst und die PowerShell neu startest. Alternativ kannst du dein Profil auch sofort neu laden, indem du diesen Befehl ausführst:
.
$profile
Funktionen sind ebenfalls sehr häufig anzutreffende Anpassungen, wobei die beliebteste die Funktion prompt
ist.
Diskussion
In der Lösung werden drei Techniken vorgestellt, mit denen du deine PowerShell-Umgebung sinnvoll anpassen kannst: Aliase, Funktionen und eine handschriftlich angepasste Eingabeaufforderung. Du kannst diese Techniken jederzeit während deiner PowerShell-Sitzung anwenden (und wirst es auch oft tun), aber dein Profilskript ist der Standardort für Anpassungen, die du in jeder Sitzung anwenden möchtest.
Hinweis
Um einen Alias oder eine Funktion zu entfernen, verwendest du das Cmdlet Remove-Item
:
Remove-Item
function
:
\
MyCustomFunction
Remove-Item
alias
:
\
new
Obwohl die Funktion prompt
eine einfache Zeichenkette zurückgibt, kannst du die Funktion auch für komplexere Aufgaben verwenden. Viele Benutzer aktualisieren zum Beispiel den Titel ihres Konsolenfensters (indem sie die Variable $host.UI.RawUI.WindowTitle
ändern) oder verwenden das Cmdlet Write-Host
, um die Eingabeaufforderung in Farbe auszugeben. Wenn deine Eingabeaufforderung die Bildschirmausgabe selbst übernimmt, muss sie trotzdem eine Zeichenkette zurückgeben (z. B. ein einzelnes Leerzeichen), damit PowerShell nicht die Standardeinstellung verwendet. Wenn du nicht möchtest, dass dieses zusätzliche Leerzeichen in deiner Eingabeaufforderung erscheint, füge ein zusätzliches Leerzeichen am Ende deines Write-Host
Befehls ein und gib das Backspace ("`b"
) Zeichen zurück, wie in Beispiel 1-4 gezeigt.
Beispiel 1-4. Ein Beispiel für eine PowerShell Eingabeaufforderung
##############################################################################
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
Set-StrictMode
-Version
3
function
prompt
{
$id
=
1
$historyItem
=
Get-History
-Count
1
if
(
$historyItem
)
{
$id
=
$historyItem
.
Id
+
1
}
Write-Host
-ForegroundColor
DarkGray
"
`n
[
$(
Get-Location
)
]"
Write-Host
-NoNewLine
"PS:$id > "
$host
.
UI
.
RawUI
.
WindowTitle
=
"
$(
Get-Location
)
"
"
`b
"
}
Unter zeigt diese Eingabeaufforderung nicht nur die aktuelle Position, sondern auch die ID des Befehls in deinem Verlauf an. So kannst du frühere Befehle relativ einfach finden und aufrufen:
[C:\] PS:73 >5 * 5 25 [C:\] PS:74 >1 + 1 2 [C:\] PS:75 >Invoke-History 73 5 * 5 25 [C:\] PS:76 >
Obwohl das Profil, auf das $profile
verweist, dasjenige ist, das du fast immer verwenden wirst, unterstützt die PowerShell eigentlich vier verschiedene Profilskripte. Weitere Einzelheiten zu diesen Skripten (und anderen Optionen zur Anpassung der Shell) findest du unter "Allgemeine Anpassungspunkte".
1.10 Anpassen des Benutzereingabeverhaltens der PowerShell
Lösung
Verwende das Set-PSReadLineOption
cmdlet, um Eigenschaften wie EditMode
(Windows, VI, Emacs) und die Verlaufsverwaltung zu konfigurieren. Zum Beispiel, um die Fortsetzungszeile für unvollständige Eingaben etwas roter als sonst zu machen:
Set-PSReadLineOption
-Colors
@{
ContinuationPrompt
=
"#663333"
}
Verwende den Befehl Set-PSReadLineKeyHandler
, um zu konfigurieren, wie PSReadLine auf deine aktuellen Tastendrücke reagiert. So kannst du zum Beispiel die Vorwärts- und Rückwärtsnavigation im Verzeichnisverlauf für Alt+Comma
und Alt+Period
hinzufügen:
Set-PSReadLineKeyHandler
-Chord
'Alt+,'
-ScriptBlock
{
Set-Location
-
[Microsoft.PowerShell.PSConsoleReadLine]
::
RevertLine
()
[Microsoft.PowerShell.PSConsoleReadLine]
::
AcceptLine
()
}
Set-PSReadLineKeyHandler
-Chord
'Alt+.'
-ScriptBlock
{
Set-Location
+
[Microsoft.PowerShell.PSConsoleReadLine]
::
RevertLine
()
[Microsoft.PowerShell.PSConsoleReadLine]
::
AcceptLine
()
}
Diskussion
Als die PowerShell zum ersten Mal auftauchte, waren die Unixer unter den ersten, die es bemerkten. Sie hatten schon seit Jahren eine leistungsfähige Shell und ein starkes Erbe der Automatisierung - und "wenn ich gezwungen bin, Windows zu benutzen, ist die PowerShell der Hammer", ist ein Satz, den wir oft gehört haben.
Diese natürliche Übernahme war kein Fehler. Viele im Team kommen aus dem Unix-Umfeld, und die Ähnlichkeiten mit traditionellen Unix-Shells waren beabsichtigt. Trotzdem hören wir von Leuten, die von anderen Shells kommen, immer noch gelegentlich, dass sich die eine oder andere Funktion seltsam anfühlt. Alt+P startet nicht das integrierte Auslagerungsprogramm? Strg+XX bewegt sich nicht zwischen dem Anfang der Zeile und der aktuellen Cursorposition? Abscheulich!
In den frühen Versionen der PowerShell gab es nichts, was du sinnvollerweise tun konntest, um dieses Problem zu lösen. In diesen Versionen las die PowerShell ihre Eingaben von der Konsole im so genannten Cooked Mode, in demdas Windows-Konsolen-Subsystem alle Tastendrücke, ausgefallene F7-Menüs und mehr verarbeitet. Wenn du die Eingabetaste oder die Tabulatortaste drückst, erhält die PowerShell den Text, den du bisher eingegeben hast, aber das war's auch schon. Sie weiß nicht, dass du die (Unix-ähnlichen) Tasten Strg+R, Strg+A, Strg+E oder eine andere Taste gedrückt hast.
In späteren Versionen der PowerShell sind die meisten dieser Fragen mit der Einführung des fantastischen PSReadLine-Moduls, das die PowerShell für die Befehlszeileneingabe verwendet, verschwunden. PSReadLine
bietet umfangreiche Syntaxhervorhebung, Tabulatorvervollständigung, Verlaufsnavigation und mehr.
Mit dem Modul PSReadLine
kannst du es in einem unglaublichen Ausmaß konfigurieren. DasSet-PSReadLineOption
cmdlet unterstützt Optionen für die Benutzeroberfläche, den Eingabemodus, die Verlaufsverarbeitung und vieles mehr:
EditMode BellStyle ContinuationPrompt CompletionQueryItems HistoryNoDuplicates WordDelimiters AddToHistoryHandler HistorySearchCaseSensitive CommandValidationHandler HistorySaveStyle HistorySearchCursorMovesToEnd HistorySavePath MaximumHistoryCount AnsiEscapeTimeout MaximumKillRingCount PromptText ShowToolTips ViModeIndicator ExtraPromptLineCount ViModeChangeHandler DingTone PredictionSource DingDuration Colors
In kannst du nicht nur das Laufzeitverhalten konfigurieren, sondern auch die Reaktion auf deine Tastendrücke festlegen. Um alle Verhaltensweisen zu sehen, die du dem Tastendruck zuordnen kannst, rufe Get-PSReadLineKeyHandler
auf. PSReadLine
bietet seitenweise Optionen - viele davon sind derzeit keinem Tastendruck zugeordnet:
PS > Get-PSReadLineKeyHandler Basic editing functions ======================= Key Function Description --- -------- ----------- Enter AcceptLine Accept the input or move to the next line if input is missing a closing token. Shift+Enter AddLine Move the cursor to the next line without attempting to execute the input Backspace BackwardDeleteChar Delete the character before the cursor Ctrl+h BackwardDeleteChar Delete the character before the cursor Ctrl+Home BackwardDeleteLine Delete text from the cursor to the start of the line Ctrl+Backspace BackwardKillWord Move the text from the start of the current or previous word to the cursor to the kill ring Ctrl+w BackwardKillWord Move the text from the start of the current or previous word to the cursor to... (...)
Um für eine dieser Funktionen zu konfigurieren, verwende den Befehl Set-PSReadLineKeyHandler
. Um zum Beispiel Ctrl+Shift+C
so einzustellen, dass farbige Bereiche des Puffers in die Zwischenablage aufgenommen werden, führe den Befehl aus:
Set-PSReadLineKeyHandler
-Chord
Ctrl
+
Shift
+
C
-Function
CaptureScreen
Wenn es unter keine vordefinierte Funktion gibt, die das tut, was du willst, kannst du mit dem-ScriptBlock
Parameter verwenden, damit PSReadLine
einen beliebigen Code deiner Wahl ausführt, wenn du eine Taste oder Tastenkombination drückst. Das Beispiel in der Lösung demonstriert dies, indem es die Navigation im Verzeichnisverlauf vorwärts und rückwärts ermöglicht.
Damit diese Änderungen bestehen bleiben, fügst du einfach diese Befehle zu deinem PowerShell-Profil hinzu.
Obwohl eigentlich nur für extrem fortgeschrittene Szenarien gedacht ist, da PSReadLine
fast alles abdeckt, was du jemals brauchen könntest, kannst du diese Funktionalität mit der Funktion PSConsoleHostReadLine
noch weiter anpassen oder erweitern. Wenn du diese Methode im PowerShell-Konsolenhost definierst, ruft die PowerShell diese Funktion anstelle der standardmäßigen Cooked Mode-Eingabefunktion von Windows auf. Die Standardversion dieser Funktion ruft den PSReadLine
ReadLine
input handler auf. Wenn du diese Funktion jedoch komplett neu definieren möchtest, ist das alles - der Rest ist dir überlassen. Wenn du eine benutzerdefinierte Eingabemethode implementieren möchtest, liegt die Freiheit (und die Verantwortung) ganz bei dir.
Wenn du diese Funktion definierst, muss sie die Benutzereingabe verarbeiten und den resultierenden Befehl zurückgeben. Beispiel 1-5 implementiert einen etwas lächerlichen Notepad-basierten Benutzereingabemechanismus:
Beispiel 1-5. Ein Notepad-basierter Benutzereingabemechanismus
function
PSConsoleHostReadLine
{
$inputFile
=
Join-Path
$env:TEMP
PSConsoleHostReadLine
Set-Content
$inputFile
"PS > "
## Notepad opens. Enter your command in it, save the file,
## and then exit.
notepad
$inputFile
|
Out-Null
$userInput
=
Get-Content
$inputFile
$resultingCommand
=
$userInput
.
Replace
(
"PS >"
,
""
)
$resultingCommand
}
Unter findest du weitere Informationen zum Umgang mit Tastatureingaben und anderen Formen der Benutzereingabe, siehe Kapitel 13.
1.11 Anpassen des Befehlsauflösungsverhaltens der PowerShell
Lösung
Weisen Sie einen Skriptblock einer oder allen PreCommandLookupAction
, PostCommandLookupAction
oder CommandNotFoundAction
Eigenschaften von $executionContext.SessionState.InvokeCommand
zu. Beispiel 1-6 ermöglicht eine einfache Navigation im übergeordneten Verzeichnis, wenn Sie mehrere Punkte eingeben.
Diskussion
Wenn du einen Befehl in der PowerShell aufrufst, durchläuft die Engine drei verschiedene Phasen:
-
Rufe den Text des Befehls ab.
-
Finde den Befehl für diesen Text.
-
Rufe den Befehl auf, der gefunden wurde.
In der PowerShell kannst du mit der Eigenschaft $executionContext.SessionState.InvokeCommand
jede dieser Phasen mit Skriptblöcken überschreiben, um eine oder alle Phasen abzufangen.PreCommandLookupAction
, PostCommandLookupAction
oder CommandNotFoundAction
abzufangen.
Jeder Skriptblock erhält zwei Parameter: den Befehlsnamen und ein Objekt(CommandLookupEventArgs
), das das Verhalten der Befehlssuche steuert. Wenn dein Handler einen Skriptblock der Eigenschaft CommandScriptBlock
des CommandLookupEventArgs
zuweist oder ein CommandInfo
der Eigenschaft Command
des CommandLookupEventArgs
zuweist, verwendet PowerShell diesen Skriptblock bzw. diesen Befehl. Wenn dein Skriptblock die Eigenschaft StopSearch
auf true
setzt, führt PowerShell keine weitere Befehlsauflösung durch.
PowerShell ruft den Skriptblock PreCommandLookupAction
auf, wenn sie den Namen eines Befehls kennt (z. B. Get-Process
), aber noch nicht nach dem Befehl selbst gesucht hat. Du kannst diese Aktion außer Kraft setzen, wenn du in erster Linie auf den Text des Befehlsnamens reagieren oder die reguläre Befehls- oder Aliasauflösung der PowerShell umgehen möchtest. Beispiel 1-7 demonstriert eine PreCommandLookupAction
, die nach Befehlen mit einem Sternchen vor dem Namen sucht. Wenn er einen findet, aktiviert er den-Verbose
Parameter.
Beispiel 1-7. Anpassen der PreCommandLookupAction
$executionContext.SessionState.InvokeCommand.PreCommandLookupAction = { param($CommandName, $CommandLookupEventArgs) ## If the command name starts with an asterisk, then ## enable its Verbose parameter if($CommandName -match "\*") { ## Remove the leading asterisk $NewCommandName = $CommandName -replace '\*','' ## Create a new script block that invokes the actual command, ## passes along all original arguments, and adds in the -Verbose ## parameter $CommandLookupEventArgs.CommandScriptBlock = { & $NewCommandName @args -Verbose ## We call GetNewClosure() so that the reference to $NewCommandName ## can be used in the new command. }.GetNewClosure() } } PS > dir > 1.txt PS > dir > 2.txt PS > del 1.txt PS > *del 2.txt VERBOSE: Performing operation "Remove file" on Target "C:\temp\tempfolder\2.txt".
Nachdem die PowerShell den PreCommandLookupAction
ausgeführt hat (falls er existiert und keinen Befehl zurückgibt), durchläuft sie ihre reguläre Befehlsauflösung. Wenn sie einen Befehl findet, ruft sie den Skriptblock auf, der mit PostCommandLookupAction
verknüpft ist. Du kannst diese Aktion außer Kraft setzen, wenn du in erster Linie auf einen Befehl reagieren willst, der gerade aufgerufen werden soll. Beispiel 1-8 zeigt ein PostCommandLookupAction
, das die von dir am häufigsten verwendeten Befehle auflistet.
Beispiel 1-8. Anpassen der PostCommandLookupAction
$executionContext.SessionState.InvokeCommand.PostCommandLookupAction = { param($CommandName, $CommandLookupEventArgs) ## Stores a hashtable of the commands we use most frequently if(-not (Test-Path variable:\CommandCount)) { $global:CommandCount = @{} } ## If it was launched by us (rather than as an internal helper ## command), record its invocation. if($CommandLookupEventArgs.CommandOrigin -eq "Runspace") { $commandCount[$CommandName] = 1 + $commandCount[$CommandName] } } PS > Get-Variable commandCount PS > Get-Process -id $pid PS > Get-Process -id $pid PS > $commandCount Name Value ---- ----- Out-Default 4 Get-Variable 1 prompt 4 Get-Process 2
Wenn die Befehlsauflösung nicht erfolgreich ist, ruft die PowerShell den Skriptblock CommandNotFoundAction
auf, sofern ein solcher vorhanden ist. Im einfachsten Fall kannst du diese Aktion außer Kraft setzen, wenn du das Fehlerverhalten der PowerShell, wenn sie einenBefehl nicht finden kann, aufheben oder überschreiben möchtest.
Als fortgeschrittene Anwendung kannst du mit CommandNotFoundAction
PowerShell-Erweiterungen schreiben, die ihr Verhalten anhand der Form des Namens und nicht anhand der übergebenen Argumente ändern. Du könntest zum Beispiel URLs automatisch starten, wenn du sie eingibst, oder zwischen Anbietern navigieren, indem du relative Pfadangaben eingibst.
Die Lösung enthält ein Beispiel für die Implementierung dieser Art von Handler. Die dynamische relative Pfadnavigation ist zwar keine eingebaute Funktion der PowerShell, aber es ist möglich, eine sehr vernünftige Alternative zu erhalten, indem man die CommandNotFoundAction
abfängt. Wenn wir einen fehlenden Befehl sehen, der ein Muster hat, das wir behandeln wollen (eine Reihe von Punkten), geben wir einen Skriptblock zurück, der die entsprechende relative Pfadnavigation durchführt.
1.12 Einen Befehl finden, um eine Aufgabe zu erledigen
Lösung
Verwende das Cmdlet Get-Command
, um nach Befehlen zu suchen und sie zu untersuchen.
Um die Zusammenfassung eines bestimmten Befehls zu erhalten, gibst du den Befehlsnamen als Argument an:
Get-Command CommandName
Um detaillierte Informationen über einen bestimmten Befehl zu erhalten, leiten Sie die Ausgabe vonGet-Command
an das Cmdlet Format-List
weiter:
Get-Command CommandName
| Format-List
Um nach allen Befehlen zu suchen, deren Name Text enthält, umrandest du den Text mit Sternchen:
Get-Command *text
*
Um nach allen Befehlen zu suchen, die das Verb Get
verwenden, gibst du Get
an den-Verb
Parameter an:
Get-Command
-Verb
Get
Um nach allen Befehlen zu suchen, die auf einen Dienst wirken, verwende Service
als Wert für den Parameter-Noun
:
Get-Command -Noun Service
Diskussion
Einer der Vorteile, die PowerShell Administratoren bietet, ist die Konsistenz der Befehlsnamen. Alle PowerShell-Befehle ( Cmdlets genannt) folgen einem regelmäßigen Verb-Nomen-Muster - zum Beispiel Get-Process
, Get-Service
und Set-Location
. Die Verben stammen aus einer relativ kleinen Gruppe von Standardverben (siehe Anhang J) und beschreiben, welche Aktion das Cmdlet ausführt. Die Substantive sind spezifisch für das Cmdlet und beschreiben, worauf das Cmdlet wirkt.
Wenn du diese Philosophie kennst, kannst du leicht lernen, mit Gruppen von Cmdlets zu arbeiten. Wenn du einen Dienst auf dem lokalen Rechner starten willst, ist das Standardverb dafür Start
. Am besten versuchst du es zuerst mit Start-Service
(was in diesem Fall richtig wäre), aber auch die Eingabe von Get-Command -Verb Start
ist ein guter Weg, um zu sehen, welche Dinge du starten kannst. Umgekehrt kannst du sehen, welche Aktionen von Diensten unterstützt werden, indem du Get-Command -Noun Service
eingibst.
Wenn du das Cmdlet Get-Command
verwendest, gibt die PowerShell Ergebnisse aus der Liste aller auf deinem System verfügbaren Befehle zurück. Wenn du stattdessen nur nach Befehlen aus Modulen suchen möchtest, die du entweder explizit oder durch automatisches Laden geladen hast, verwende den-ListImported
Parameter. Weitere Informationen über das automatische Laden von Befehlen in der PowerShell findest du in Rezept 1.28.
In Rezept 1.13 findest du eine Liste aller Befehle und eine kurze Beschreibung ihrer Funktionen.
Das Cmdlet Get-Command
ist einer der drei Befehle, die du am häufigsten verwenden wirst, wenn du die PowerShell kennenlernst. Die anderen beiden Befehle sind Get-Help
und Get-Member
.
gibt es einen wichtigen Punkt, den du beachten musst, wenn du nach einem PowerShell-Befehl suchst, um eine bestimmte Aufgabe zu erledigen. Oft gibt es diesen PowerShell-Befehl gar nicht, weil die Aufgabe am besten auf dieselbe Art und Weise wie bisher erledigt wird- zum Beispiel ipconfig.exe
, um IP-Konfigurationsinformationen abzurufen, netstat.exe
, um Protokollstatistiken und aktuelle TCP/IP-Netzwerkverbindungen aufzulisten, und viele mehr.
Weitere Informationen über das Cmdlet Get-Command
findest du, indem du Get-Help Get-Command
.
Siehe auch
1.13 Hilfe zu einem Befehl erhalten
Lösung
Der Befehl, der Hilfe und Nutzungsinformationen zu einem Befehl liefert, heißt Get-Help
. Er unterstützt verschiedene Ansichten der Hilfeinformationen, je nach deinen Bedürfnissen.
Um eine Zusammenfassung der Hilfeinformationen für einen bestimmten Befehl zu erhalten, gib den Namen des Befehls als Argument für das Cmdlet Get-Help
an. Dazu gehören vor allem die Zusammenfassung, die Syntax und die detaillierte Beschreibung:
Get-Help CommandName
oder:
CommandName
-?
Um die detaillierten Hilfeinformationen für einen bestimmten Befehl zu erhalten, gibst du das Flag -Detailed
an das Cmdlet Get-Help
an. Zusätzlich zur Übersichtsansicht werden auch die Parameterbeschreibungen und Beispiele angezeigt:
Get-Help CommandName
-Detailed
Um die vollständigen Hilfeinformationen für einen bestimmten Befehl zu erhalten, gibst du dem Cmdlet Get-Help
das Flag -Full
mit. Neben der detaillierten Ansicht enthält dies auch die vollständigen Parameterbeschreibungen und zusätzliche Hinweise:
Get-Help CommandName
-Full
Um nur die Beispiele für einen bestimmten Befehl zu erhalten, gibst du das Flag -Examples
an das Cmdlet Get-Help
an:
Get-Help CommandName
-Examples
Um die aktuellste Online-Version des Hilfethemas eines Befehls abzurufen, gibst du dem Cmdlet Get-Help
das Flag -Online
mit:
Get-Help CommandName
-Online
Um eine durchsuchbare, grafische Ansicht eines Hilfethemas anzuzeigen, verwende den Parameter -ShowWindow
:
Get-Help CommandName
-ShowWindow
Um alle Hilfethemen zu finden, die ein bestimmtes Schlüsselwort enthalten, gibst du dieses Schlüsselwort als Argument an das Cmdlet Get-Help
an. Wenn das Schlüsselwort nicht auch der Name eines bestimmten Hilfethemas ist, werden alle Hilfethemen zurückgegeben, die das Schlüsselwort enthalten, einschließlich des Namens, der Kategorie und der Synopsis:
Get-Help Keyword
Diskussion
Das Cmdlet Get-Help
ist die wichtigste Methode zur Interaktion mit dem Hilfesystem in der PowerShell. Wie das Cmdlet Get-Command
unterstützt auch das Cmdlet Get-Help
Wildcards. Wenn du alle Befehle auflisten möchtest, deren Hilfeinhalt mit einem bestimmten Muster übereinstimmt (z. B. process), kannst du einfach Folgendes eingeben:
Get-Help
*
process
*
Wenn das Muster nur auf einen einzigen Befehl zutrifft, zeigt PowerShell die Hilfe für diesen Befehl an. Obwohl die Suche mit Befehlsplatzhaltern und Schlüsselwörtern eine hilfreiche Methode ist, die PowerShell-Hilfe zu durchsuchen, findest du in Rezept 1.15 ein Skript, mit dem du den Inhalt der Hilfe nach einem bestimmten Muster durchsuchen kannst.
Während dir Tausende von Seiten benutzerdefinierter Hilfeinhalte zur Verfügung stehen, enthält die PowerShell standardmäßig nur Informationen, die sie automatisch aus den in den Befehlen selbst enthaltenen Informationen generieren kann: Namen, Parameter, Syntax und Parametervorgaben. Du musst deinen Hilfeinhalt aktualisieren, um den Rest zu erhalten. Wenn du Get-Help
für einen Befehl aufrufst, für den du keinen Hilfeinhalt heruntergeladen hast, siehst du die folgenden Hinweise als Teil der Hilfe:
REMARKS Get-Help cannot find the Help files for this cmdlet on this computer. It is displaying only partial help. -- To download and install Help files for the module that includes this cmdlet, use Update-Help. -- To view the Help topic for this cmdlet online, type: "Get-Help Get-Process -Online" or go to https://go.microsoft.com/fwlink/?LinkID=2096814.
Führe das Cmdlet Update-Help
aus, und PowerShell lädt automatisch die neuesten Hilfeinhalte für alle Module auf deinem System herunter und installiert sie. Weitere Informationen zur aktualisierbaren Hilfe findest du in Rezept 1.14.
Wenn du eine Liste aller Cmdlets und Aliase (zusammen mit einer kurzen Zusammenfassung) erstellen möchtest, führe den folgenden Befehl aus:
Get-Help
*
-Category
Cmdlet
|
Select-Object
Name
,
Synopsis
|
Format-Table
-Auto
Neben der konsolenbasierten Hilfe bietet die PowerShell auch einen Online-Zugang zu ihren Hilfeinhalten. Die Lösung zeigt, wie du schnell auf Online-Hilfeinhalte zugreifen kannst.
Das Cmdlet Get-Help
ist einer der drei Befehle, die du am häufigsten verwenden wirst, wenn du die PowerShell kennenlernst. Die anderen beiden Befehle sind Get-Command
und Get-Member
.
Weitere Informationen über das Cmdlet Get-Help
findest du, indem du Get-Help Get-Help
.
Siehe auch
1.14 Inhalt der Systemhilfe aktualisieren
Diskussion
Eine der größten Stärken der PowerShell ist der unglaublich detaillierte Inhalt der Hilfe. Zählt man nur den Hilfeinhalt und die about_*
Themen, die die Kernfunktionen beschreiben, umfasst die PowerShell-Hilfe etwa eine halbe Million Wörter und würde in gedruckter Form 1.200 Seiten umfassen.
Die Herausforderung, mit der jede Version der PowerShell zu kämpfen hat, besteht darin, dass diese Hilfeinhalte zur gleichen Zeit wie die PowerShell selbst geschrieben werden. Da es das Ziel ist, dem Benutzer zu helfen, ist der Inhalt, der zum Zeitpunkt der Veröffentlichung einer PowerShell-Version fertig ist, eine bestmögliche Schätzung dessen, wozu die Benutzer Hilfe benötigen werden.
Sobald die Benutzer die PowerShell in die Hände bekommen, haben sie Fragen. Einige davon werden in den Hilfethemen beantwortet, andere wiederum nicht. Manchmal ist die Hilfe aufgrund einer Produktänderung während der Veröffentlichung einfach falsch. Deshalb unterstützt die PowerShell die aktualisierbare Hilfe.
Es ist nicht nur möglich, die Hilfe zu aktualisieren, sondern der Befehl Update-Help
ist sogar die einzige Möglichkeit, Hilfe zu deinem System zu erhalten. Die PowerShell bietet von Haus aus nur das, was in den Befehlen selbst enthalten ist: Name, Syntax, Parameter und Standardwerte.
Get-Help
Wenn du für einen Befehl aufrufst, für den du keinen Hilfeinhalt heruntergeladen hast, siehst du die folgenden Hinweise als Teil der Hilfe:
REMARKS Get-Help cannot find the Help files for this cmdlet on this computer. It is displaying only partial help. -- To download and install Help files for the module that includes this cmdlet, use Update-Help. -- To view the Help topic for this cmdlet online, type: "Get-Help Get-Process -Online" or go to https://go.microsoft.com/fwlink/?LinkID=2096814.
Wenn du das Cmdlet Update-Help
ausführst, lädt PowerShell automatisch die neuesten Hilfeinhalte für alle Module auf deinem System herunter und installiert sie.
Wenn du Update-Help
aufrufst, prüft PowerShell jedes Modul auf deinem System und vergleicht die Hilfe, die du für dieses Modul hast, mit der neuesten Online-Version. Für Inbox-Module verwendet die PowerShell download.microsoft.com
, um aktualisierte Hilfeinhalte abzurufen. Andere Module, die du aus dem Internet herunterlädst, können den Modulschlüssel HelpInfoUri
verwenden, um ihre eigene aktualisierbare Hilfe zu unterstützen.
PowerShell speichert diesen Inhalt im Verzeichnis PowerShell\Help
in deinen Benutzerdokumenten oder deinem Home-Verzeichnis.
Standardmäßig bezieht der Befehl Update-Help
seinen Inhalt aus dem Internet. Wenn du die Hilfe auf einem Rechner aktualisieren möchtest, der nicht mit dem Internet verbunden ist, kannst du den-SourcePath
Parameter des Cmdlets Update-Help
verwenden. Dieser Pfad stellt ein Verzeichnis oder einen UNC-Pfad dar, in dem PowerShell nach aktualisierten Hilfeinhalten suchen soll. Um diesen Inhalt aufzufüllen, lädst du die Dateien zunächst mit dem Cmdlet Save-Help
herunter und kopierst sie dann an den Quellort.
Weitere Informationen zur PowerShell-Hilfe findest du in Rezept 1.13.
1.15 Programm: Suchhilfe für Text
Mit den beiden Cmdlets , Get-Command
und Get-Help
kannst du nach Befehlsnamen suchen, die einem bestimmten Muster entsprechen. Wenn du jedoch nicht genau weißt, nach welchen Teilen eines Befehlsnamens du suchst, wirst du häufiger Erfolg haben, wenn du den Inhalt der Hilfe nach einer Antwort durchsuchst. Auf Unix-Systemen heißt dieser Befehl Apropos
.
Das Cmdlet Get-Help
sucht automatisch in der Hilfedatenbank nach Schlüsselwortreferenzen, wenn es kein Hilfethema für das von dir angegebene Argument finden kann. Du kannst die Suche aber auch noch weiter ausdehnen, um nach Textmustern oder sogar nach Hilfethemen zu suchen, die sich auf bestehende Hilfethemen beziehen. Die PowerShell-Hilfe unterstützt eine Version der Suche mit Platzhaltern, aber keine vollständigen regulären Ausdrücke.
Das muss uns aber nicht aufhalten, denn wir können die Funktionalität selbst schreiben.
Um dieses Programm auszuführen, gibst du einen Suchstring in das Skript Search-Help
ein (siehe Beispiel 1-9). Der Suchstring kann entweder einfacher Text oder ein regulärer Ausdruck sein. Das Skript zeigt dann den Namen und die Zusammenfassung aller Hilfethemen an, die übereinstimmen. Um den Inhalt der Hilfe zu diesem Thema zu sehen, verwende das Cmdlet Get-Help
.
Beispiel 1-9. Search-Help.ps1
##############################################################################
##
## Search-Help
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Search the PowerShell help documentation for a given keyword or regular
expression. For simple keyword searches in PowerShell version two or three,
simply use "Get-Help <keyword>"
.EXAMPLE
PS > Search-Help hashtable
Searches help for the term 'hashtable'
.EXAMPLE
PS > Search-Help "(datetime|ticks)"
Searches help for the term datetime or ticks, using the regular expression
syntax.
#>
param
(
## The pattern to search for
[
Parameter
(
Mandatory
=
$true
)]
$Pattern
)
$helpNames
=
$(
Get-Help
*
|
Where-Object
{
$_
.
Category
-ne
"Alias"
})
## Go through all of the help topics
foreach
(
$helpTopic
in
$helpNames
)
{
## Get their text content, and
$content
=
Get-Help
-Full
$helpTopic
.
Name
|
Out-String
if
(
$content
-match
"(.{0,30}$pattern.{0,30})"
)
{
$helpTopic
|
Add-Member
NoteProperty
Match
$matches
[
0
].
Trim
()
$helpTopic
|
Select-Object
Name
,
Match
}
}
Weitere Informationen zum Ausführen von Skripten findest du in Rezept 1.2.
1.16 PowerShell an einem bestimmten Ort starten
Diskussion
Wenn du das Dateisystem mit dem Windows Explorer durchsuchst, gibst du pwsh.exe
oderpowershell.exe
in die Adressleiste ein, um die PowerShell an dieser Stelle zu starten (siehe Abbildung 1-2).
Beachte, dass deine Eingaben mit der Erweiterung .exe
enden müssen, sonst öffnet der Explorer in der Regel deinen PowerShell
Dokumentenordner. Außerdem kannst du die Windows PowerShell direkt über das Menü Datei öffnen, wie in Abbildung 1-3 gezeigt.
Unter findest du eine weitere Möglichkeit, die PowerShell aus dem Windows Explorer zu starten: Windows Terminal (wenn du es installiert hast) fügt die Option "In Windows Terminal öffnen" hinzu, wenn du mit der rechten Maustaste auf einen Ordner im Windows Explorer klickst.
Wenn du den gewünschten Ordner nicht mit dem Windows Explorer durchsuchst, kannst du die PowerShell über Start→Ausführen (oder eine andere Möglichkeit, eine Anwendung zu starten) an einem bestimmten Ort starten. Verwende dazu den -NoExit
Parameter der PowerShell zusammen mit dem-Command
Parameter. Im Parameter -Command
rufst du das Cmdlet Set-Location
auf, um zunächst den gewünschten Speicherort aufzurufen.
pwsh -NoExit -Command Set-Location 'C:\Program Files'
1.17 Aufrufen eines PowerShell-Befehls oder -Skripts von außerhalb der PowerShell
Lösung
Um einen PowerShell-Befehl aufzurufen, verwendest du den Parameter -Command
:
pwsh
-Command
Get-Process
;
Read-Host
Um ein PowerShell-Skript zu starten, verwendest du den Parameter -File
:
pwsh -File 'full path to script' arguments
Zum Beispiel:
pwsh
-File
'c:\shared scripts\Get-Report.ps1'
Hello
World
Diskussion
In der Voreinstellung werden alle Argumente an pwsh.exe als auszuführendes Skript interpretiert. Wenn du den Parameter -Command
verwendest, führt PowerShell den Befehl so aus, als hättest du ihn in der interaktiven Shell eingegeben, und beendet sich dann. Du kannst dieses Verhalten anpassen, indem du andere Parameter an pwsh.exe übergibst, z. B. -NoExit
, -NoProfile
, und andere.
Hinweis
Wenn du der Autor eines Programms bist, das PowerShell-Skripte oder -Befehle ausführen muss, kannst du diese Skripte und Befehle mit der PowerShell viel einfacher aufrufen als mit der Befehlszeilenschnittstelle. Weitere Informationen zu diesem Ansatz findest du in Rezept 17.10.
Da das Starten eines Skripts so häufig vorkommt, bietet PowerShell den Parameter -File
an, um die Komplexität zu beseitigen, die entsteht, wenn man ein Skript über den-Command
Parameter. Mit dieser Technik kannst du ein PowerShell-Skript als Ziel eines Anmeldeskripts, einer erweiterten Dateiverknüpfung, einer geplanten Aufgabe und vielem mehr aufrufen.
Hinweis
Wenn die PowerShell feststellt, dass ihre Eingabe- oder Ausgabeströme umgeleitet wurden, unterdrückt sie alle Eingabeaufforderungen, die sie normalerweise anzeigen würde. Wenn du eine interaktive Eingabeaufforderung der PowerShell in einer anderen Anwendung (z. B. Emacs) bereitstellen möchtest, verwende -
als Argument für den Parameter -File
. In der PowerShell (wie auch in traditionellen Unix-Shells) bedeutet dies "von der Standardeingabe übernommen".
pwsh -File -
Wenn das Skript für die Hintergrundautomatisierung oder eine geplante Aufgabe ist, können diese Skripte manchmal die Umgebung des Benutzers stören (oder von ihr beeinflusst werden). Für diese Situationen sind drei Parameter nützlich:
-NoProfile
-
Führt den Befehl oder das Skript aus, ohne Benutzerprofilskripte zu laden. Dadurch wird das Skript schneller gestartet, aber vor allem wird verhindert, dass Benutzereinstellungen (z. B. Aliase und Einstellungsvariablen) die Arbeitsumgebung des Skripts beeinträchtigen.
-WindowStyle
-
Führt den Befehl oder das Skript mit dem angegebenen Fenstermodell aus - in der Regel
Hidden
. Wenn es mit dem FenstermodellHidden
ausgeführt wird, blendet PowerShell das Hauptfenster sofort aus. Weitere Möglichkeiten zur Steuerung des Fensterstils in der PowerShell findest du in Rezept 24.3. -ExecutionPolicy
-
Führt den Befehl oder das Skript mit einer bestimmten Ausführungsrichtlinie aus, die nur für diese Instanz von PowerShell gilt. So kannst du PowerShell-Skripte zur Verwaltung eines Systems schreiben, ohne die systemweite Ausführungsrichtlinie ändern zu müssen. Weitere Informationen über Ausführungsrichtlinien finden Sie in Rezept 18.1.
Wenn die Argumente für den Parameter -Command
komplex werden, kann die Sonderzeichenbehandlung in der Anwendung, die die PowerShell aufruft (z. B. cmd.exe), den Befehl, den du an die PowerShell senden willst, beeinträchtigen. Für diese Situation unterstützt PowerShell einen EncodedCommand
Parameter: eine Base64-kodierte Darstellung der Unicode-Zeichenfolge, die du ausführen möchtest. Beispiel 1-10 zeigt wie du einen String mit PowerShell-Befehlen in eine Base64-kodierte Form umwandelst.
Beispiel 1-10. PowerShell-Befehle in eine Base64-kodierte Form umwandeln
$commands
=
'1..10 | % { "PowerShell Rocks" }'
$bytes
=
[System.Text.Encoding]
::
Unicode
.
GetBytes
(
$commands
)
$encodedString
=
[Convert]
::
ToBase64String
(
$bytes
)
Sobald du die verschlüsselte Zeichenkette hast, kannst du sie als Wert für den Parameter EncodedCommand
verwenden, wie in Beispiel 1-11 gezeigt.
Beispiel 1-11. Starten der PowerShell mit einem verschlüsselten Befehl aus cmd.exe
Microsoft Windows [Version 10.0.19041.685] (c) 2020 Microsoft Corporation. All rights reserved. C:\Users\Lee>PowerShell -EncodedCommand MQAuAC4AMQAwACAAfAAgACUAIAB7ACAAIgBQAG8A dwBlAHIAUwBoAGUAbABsACAAUgBvAGMAawBzACIAIAB9AA== PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks PowerShell Rocks
Unter findest du weitere Informationen zum Ausführen von Skripten, siehe Rezept 1.2.
1.18 Verstehen und Anpassen der Registerkartenvervollständigung der PowerShell
Diskussion
Wenn du die Tabulatortaste drückst, ruft die PowerShell eine Funktion auf, die als Tabulator-Erweiterung bekannt ist: Sie ersetzt das, was du bisher eingegeben hast, durch eine erweiterte Version davon (falls zutreffend). Wenn du zum Beispiel Set-Location C:\
eingibst und dann die Tabulatortaste drückst, beginnt die PowerShell, die Verzeichnisse unter C:\
zu durchlaufen, in die du navigieren kannst.
Die Funktionen der integrierten PowerShell-Tab-Erweiterung sind sehr umfangreich, wie in Tabelle 1-2 gezeigt wird.
Beschreibung | Beispiel |
---|---|
Befehlsvervollständigung. Vervollständigt Befehlsnamen, wenn der aktuelle Text einen Befehlsaufruf darstellt. |
|
Parameterabschluss. Vervollständigt die Befehlsparameter für den aktuellen Befehl. |
|
Argumentvervollständigung. Vervollständigt die Befehlsargumente für den aktuellen Befehlsparameter. Dies gilt für jedes Befehlsargument, das einen festen Satz von Werten annimmt (Aufzählungen oder Parameter, die ein |
|
Vervollständigung des Textes in der Historie. Ersetzt die aktuelle Eingabe durch Elemente aus der Befehlshistorie, die mit dem Text nach dem Zeichen |
|
Abschluss der History ID. Ersetzt die aktuelle Eingabe durch die Befehlszeile aus dem Eintrag Nummer |
|
Dateinamenvervollständigung. Ersetzt den aktuellen Parameterwert durch Dateinamen, die mit dem übereinstimmen, was du bisher eingegeben hast. Wenn du das Cmdlet |
|
Operator-Vervollständigung. Ersetzt den aktuellen Text durch einen passenden Operator. Dies gilt auch für Flags, die der Anweisung |
|
Variablenvervollständigung. Ersetzt den aktuellen Text durch verfügbare PowerShell-Variablen. PowerShell bezieht sogar Variablen aus Skriptinhalten ein, die noch nie aufgerufen wurden. |
|
Member Ergänzung. Ersetzt die Membernamen für die aktuell referenzierte Variable oder den Typ. Wenn PowerShell die Member aus vorherigen Befehlen in der Pipeline ableiten kann, unterstützt sie sogar die Membervervollständigung innerhalb von Skriptblöcken. |
|
Typvervollständigung. Ersetzt abgekürzte Typennamen durch ihren Namespace-qualifizierten Namen. |
|
Wenn du die PowerShell-Funktionen zur Tabulator-Erweiterung erweitern möchtest, definiere eine Funktion namens TabExpansion2
. Du kannst diese Funktion direkt zu deinem PowerShell-Profil hinzufügen oder sie per Dot-Source aus deinem Profil beziehen. Beispiel 1-12 zeigt ein Beispiel für eine benutzerdefinierte Tabulator-Erweiterungsfunktion, die die bereits in der PowerShell integrierten Funktionen erweitert.
Beispiel 1-12. Eine Beispielimplementierung von TabExpansion2
##############################################################################
##
## TabExpansion2
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
function
TabExpansion2
{
[
CmdletBinding
(
DefaultParameterSetName
=
'ScriptInputSet'
)]
Param
(
[
Parameter
(
ParameterSetName
=
'ScriptInputSet'
,
Mandatory
=
$true
,
Position
=
0
)]
[string]
$inputScript
,
[
Parameter
(
ParameterSetName
=
'ScriptInputSet'
,
Mandatory
=
$true
,
Position
=
1
)]
[int]
$cursorColumn
,
[
Parameter
(
ParameterSetName
=
'AstInputSet'
,
Mandatory
=
$true
,
Position
=
0
)]
[System.Management.Automation.Language.Ast]
$ast
,
[
Parameter
(
ParameterSetName
=
'AstInputSet'
,
Mandatory
=
$true
,
Position
=
1
)]
[System.Management.Automation.Language.Token[]]
$tokens
,
[
Parameter
(
ParameterSetName
=
'AstInputSet'
,
Mandatory
=
$true
,
Position
=
2
)]
[System.Management.Automation.Language.IScriptPosition]
$positionOfCursor
,
[
Parameter
(
ParameterSetName
=
'ScriptInputSet'
,
Position
=
2
)]
[
Parameter
(
ParameterSetName
=
'AstInputSet'
,
Position
=
3
)]
[Hashtable]
$options
=
$null
)
End
{
## Create a new 'Options' hashtable if one has not been supplied.
## In this hashtable, you can add keys for the following options, using
## $true or $false for their values:
##
## IgnoreHiddenShares - Ignore hidden UNC shares (such as \\COMPUTER\ADMIN$)
## RelativePaths - When expanding filenames and paths, $true forces PowerShell
## to replace paths with relative paths. When $false, forces PowerShell to
## replace them with absolute paths. By default, PowerShell makes this
## decision based on what you had typed so far before invoking tab completion.
## LiteralPaths - Prevents PowerShell from replacing special file characters
## (such as square brackets and back-ticks) with their escaped equivalent.
if
(
-not
$options
)
{
$options
=
@{}
}
## Demonstrate some custom tab expansion completers for parameters.
## This is a hash table of parameter names (and optionally cmdlet names)
## that we add to the $options hashtable.
##
## When PowerShell evaluates the script block, $args gets the
## following: command name, parameter, word being completed,
## AST of the command being completed, and currently-bound arguments.
$options
[
"CustomArgumentCompleters"
]
=
@{
"Get-ChildItem:Filter"
=
{
"*.ps1"
,
"*.txt"
,
"*.doc"
}
"ComputerName"
=
{
"ComputerName1"
,
"ComputerName2"
,
"ComputerName3"
}
}
## Also define a completer for a native executable.
## When PowerShell evaluates the script block, $args gets the
## word being completed, and AST of the command being completed.
$options
[
"NativeArgumentCompleters"
]
=
@{
"attrib"
=
{
"+R"
,
"+H"
,
"+S"
}
}
## Define a "quick completions" list that we'll cycle through
## when the user types '!!' followed by TAB.
$quickCompletions
=
@(
'Get-Process -Name PowerShell | ? Id -ne $pid | Stop-Process'
,
'Set-Location $pshome'
,
(
'$errors = $error | % { $_.InvocationInfo.Line }; Get-History | '
+
' ? { $_.CommandLine -notin $errors }'
)
)
## First, check the built-in tab completion results
$result
=
$null
if
(
$psCmdlet
.
ParameterSetName
-eq
'ScriptInputSet'
)
{
$result
=
[System.Management.Automation.CommandCompletion]
::
CompleteInput
(
<#inputScript#>
$inputScript
,
<#cursorColumn#>
$cursorColumn
,
<#options#>
$options
)
}
else
{
$result
=
[System.Management.Automation.CommandCompletion]
::
CompleteInput
(
<#ast#>
$ast
,
<#tokens#>
$tokens
,
<#positionOfCursor#>
$positionOfCursor
,
<#options#>
$options
)
}
## If we didn't get a result
if
(
$result
.
CompletionMatches
.
Count
-eq
0
)
{
## If this was done at the command-line or in a remote session,
## create an AST out of the input
if
(
$psCmdlet
.
ParameterSetName
-eq
'ScriptInputSet'
)
{
$ast
=
[System.Management.Automation.Language.Parser]
::
ParseInput
(
$inputScript
,
[ref]
$tokens
,
[ref]
$null
)
}
## In this simple example, look at the text being supplied.
## We could do advanced analysis of the AST here if we wanted,
## but in this case just use its text. We use a regular expression
## to check if the text started with two exclamations, and then
## use a match group to retain the rest.
$text
=
$ast
.
Extent
.
Text
if
(
$text
-match
'^!!(.*)'
)
{
## Extract the rest of the text from the regular expression
## match group.
$currentCompletionText
=
$matches
[
1
].
Trim
()
## Go through each of our quick completions and add them to
## our completion results. The arguments to the completion results
## are the text to be used in tab completion, a potentially shorter
## version to use for display (i.e.: intellisense in the ISE),
## the type of match, and a potentially more verbose description to
## be used as a tool tip.
$quickCompletions
|
Where-Object
{
$_
-match
$currentCompletionText
}
|
Foreach
-Object
{
$result
.
CompletionMatches
.
Add
(
(
New-Object
Management
.
Automation
.
CompletionResult
$_
,
$_
,
"Text"
,
$_
)
)
}
}
}
return
$result
}
}
1.19 Programm: Aliase für gängige Befehle lernen
Bei der interaktiven Verwendung von sind vollständige Cmdlet-Namen (wie Get-ChildItem
) umständlich und langsam in der Eingabe. Obwohl Aliase viel effizienter sind, dauert es eine Weile, sie zu entdecken. Um Aliase leichter zu erlernen, kannst du deine Eingabeaufforderung so ändern, dass sie dich an die kürzere Version der von dir verwendeten Aliase erinnert.
Dies umfasst zwei Schritte:
-
Füge das in Beispiel 1-13 gezeigte Programm
Get-AliasSuggestion.ps1
zu deinem Tools-Verzeichnis oder einem anderen Verzeichnis hinzu.Beispiel 1-13. Get-AliasSuggestion.ps1
##############################################################################
##
## Get-AliasSuggestion
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Get an alias suggestion from the full text of the last command. Intended to
be added to your prompt function to help learn aliases for commands.
.EXAMPLE
PS > Get-AliasSuggestion Remove-ItemProperty
Suggestion: An alias for Remove-ItemProperty is rp
#>
param
(
## The full text of the last command
$LastCommand
)
Set-StrictMode
-Version
3
$helpMatches
=
@()
## Find all of the commands in their last input
$tokens
=
[Management.Automation.PSParser]
::
Tokenize
(
$lastCommand
,
[ref]
$null
)
$commands
=
$tokens
|
Where-Object
{
$_
.
Type
-eq
"Command"
}
## Go through each command
foreach
(
$command
in
$commands
)
{
## Get the alias suggestions
foreach
(
$alias
in
Get-Alias
-Definition
$command
.
Content
)
{
$helpMatches
+=
"Suggestion: An alias for "
+
"
$(
$alias
.
Definition
)
is
$(
$alias
.
Name
)
"
}
}
$helpMatches
-
Füge den Text aus Beispiel 1-14 zu der Funktion
Prompt
in deinem Profil hinzu. Wenn du noch keinePrompt
Funktion hast, erfährst du in Rezept 1.9, wie du eine hinzufügen kannst.Beispiel 1-14. Eine nützliche Eingabeaufforderung, um dir Aliase für gängige Befehle beizubringen
function
prompt
{
## Get the last item from the history
$historyItem
=
Get-History
-Count
1
## If there were any history items
if
(
$historyItem
)
{
## Get the training suggestion for that item
$suggestions
=
@(
Get-AliasSuggestion
$historyItem
.
CommandLine
)
## If there were any suggestions
if
(
$suggestions
)
{
## For each suggestion, write it to the screen
foreach
(
$aliasSuggestion
in
$suggestions
)
{
Write-Host
"$aliasSuggestion"
}
Write-Host
""
}
}
## Rest of prompt goes here
"PS [$env:COMPUTERNAME] >"
}
Weitere Informationen zum Ausführen von Skripten findest du in Rezept 1.2.
1.20 Programm: Aliase für gängige Parameter lernen
Lösung
Verwende das Skript Get-ParameterAlias
, wie in Beispiel 1-15 gezeigt, um alle Aliase für Parameter, die vom vorherigen Befehl verwendet wurden, in deinem Sitzungsverlauf anzuzeigen.
Beispiel 1-15. Get-ParameterAlias.ps1
##############################################################################
##
## Get-ParameterAlias
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Looks in the session history, and returns any aliases that apply to
parameters of commands that were used.
.EXAMPLE
PS > dir -ErrorAction SilentlyContinue
PS > Get-ParameterAlias
An alias for the 'ErrorAction' parameter of 'dir' is ea
#>
Set-StrictMode
-Version
3
## Get the last item from their session history
$history
=
Get-History
-Count
1
if
(
-not
$history
)
{
return
}
## And extract the actual command line they typed
$lastCommand
=
$history
.
CommandLine
## Use the Tokenizer API to determine which portions represent
## commands and parameters to those commands
$tokens
=
[System.Management.Automation.PsParser]
::
Tokenize
(
$lastCommand
,
[ref]
$null
)
$currentCommand
=
$null
## Now go through each resulting token
foreach
(
$token
in
$tokens
)
{
## If we've found a new command, store that.
if
(
$token
.
Type
-eq
"Command"
)
{
$currentCommand
=
$token
.
Content
}
## If we've found a command parameter, start looking for aliases
if
((
$token
.
Type
-eq
"CommandParameter"
)
-and
(
$currentCommand
))
{
## Remove the leading "-" from the parameter
$currentParameter
=
$token
.
Content
.
TrimStart
(
"-"
)
## Determine all of the parameters for the current command.
(
Get-Command
$currentCommand
).
Parameters
.
GetEnumerator
()
|
## For parameters that start with the current parameter name,
Where-Object
{
$_
.
Key
-like
"$currentParameter*"
}
|
## return all of the aliases that apply. We use "starts with"
## because the user might have typed a shortened form of
## the parameter name.
Foreach
-Object
{
$_
.
Value
.
Aliases
|
Foreach
-Object
{
"Suggestion: An alias for the '$currentParameter' "
+
"parameter of '$currentCommand' is '$_'"
}
}
}
}
Diskussion
Um die Eingabe von Befehlsparametern zu erleichtern, lässt dich die PowerShell nur so viel von dem Befehlsparameter eingeben, wie nötig ist, um ihn von anderen Parametern des Befehls zu unterscheiden. Zusätzlich zu den von der Shell implizit unterstützten Abkürzungen können Cmdlet-Autoren auch explizite Aliase für ihre Parameter definieren - zum Beispiel CN
als Kurzform für ComputerName
.
Diese Aliasnamen sind zwar hilfreich, aber schwer zu entdecken.
Wenn du die Aliase für einen bestimmten Befehl sehen willst, kannst du seine Parameters
Sammlung aufrufen:
PS > (Get-Command New-TimeSpan).Parameters.Values | Select Name,Aliases Name Aliases ---- ------- Start {LastWriteTime} End {} Days {} Hours {} Minutes {} Seconds {} Verbose {vb} Debug {db} ErrorAction {ea} WarningAction {wa} InformationAction {infa} ErrorVariable {ev} WarningVariable {wv} InformationVariable {iv} OutVariable {ov} OutBuffer {ob} PipelineVariable {pv}
Wenn du alle Aliase für die Parameter deines vorherigen Befehls lernen willst, rufe einfach Get-ParameterAlias.ps1
auf. Damit die PowerShell dies automatisch tut, füge in deiner Eingabeaufforderung einen Aufruf an Get-ParameterAlias.ps1
hinzu.
Dieses Skript baut auf zwei Hauptfunktionen auf: Die Tokenizer-API der PowerShell und die umfangreichen Informationen, die das Cmdlet Get-Command
zurückgibt. Die Tokenizer-API der PowerShell untersucht die Eingabe und gibt die Interpretation der Eingabe durch die PowerShell zurück: Befehle, Parameter, Parameterwerte, Operatoren und mehr. Wie die umfangreichen Ausgaben der meisten PowerShell-Befehle gibt auch Get-Command
Informationen über die Parameter eines Befehls, die Parametersätze, den Ausgabetyp (falls angegeben) und vieles mehr zurück.
Weitere Informationen über die Tokenizer-API findest du in Rezept 10.10.
1.21 Zugriff und Verwaltung des Konsolenverlaufs
Lösung
Mit den in Rezept 1.9 beschriebenen Tastenkombinationen kannst du deinen Verlauf verwalten, aber die PowerShell bietet mehrere Funktionen, mit denen du noch detaillierter mit deiner Konsole arbeiten kannst.
Um die neuesten Befehle aus deiner Sitzung abzurufen, verwende das Cmdlet Get-History
(oder seinen Alias h
):
Get-History
Um einen bestimmten Befehl aus deinem Sitzungsverlauf erneut auszuführen, gibst du seine ID an das Cmdlet Invoke-History
(oder seinen Alias ihy
):
Invoke-History ID
Um die Anzahl der in deinem Sitzungsverlauf gespeicherten Befehle zu erhöhen (oder zu begrenzen), weise der Variablen $MaximumHistoryCount
einen neuen Wert zu:
$MaximumHistoryCount = Count
Um deinen Befehlsverlauf in einer Datei zu speichern, leite die Ausgabe von Get-History
an das Cmdlet Export-CliXml
weiter:
Get-History | Export-CliXml Filename
Um einen zuvor gespeicherten Befehlsverlauf zu deinem aktuellen Sitzungsverlauf hinzuzufügen, rufst du das Cmdlet Import-CliXml
auf und leitest die Ausgabe dann an das Cmdlet Add-History
weiter:
Import-CliXml Filename
| Add-History
Um alle Befehle aus deinem Sitzungsverlauf zu löschen, verwende das cmdlet Clear-History
:
Clear-History
Diskussion
Im Gegensatz zu den in Rezept 1.9 besprochenen Hotkeys für den Konsolenverlauf erzeugt das Cmdlet Get-History
umfangreiche Objekte, die Informationen über die Elemente in deinem Verlauf darstellen. Jedes Objekt enthält die ID des Eintrags, die Befehlszeile, die Zeit des Ausführungsbeginns und die Zeit des Ausführungsendes.
Sobald du die ID eines Eintrags aus der Vergangenheit kennst (wie in der Ausgabe von Get-History
), kannst du sie an Invoke-History
übergeben, um den Befehl erneut auszuführen. Die in Rezept 1.9 gezeigte Beispiel-Eingabeaufforderung macht die Arbeit mit früheren History-Einträgen einfach, da die Eingabeaufforderung für jeden Befehl die History-ID enthält, die ihn repräsentiert.
Hinweis
Du kannst leicht erkennen, wie lange der Aufruf einer Reihe von Befehlen gedauert hat, indem du dir die Eigenschaft Duration
ansiehst. Das ist eine gute Möglichkeit, um herauszufinden, wie wenig Zeit du für die Befehle gebraucht hast, die dir stundenlange manuelle Arbeit erspart haben:
PS:29 > Get-History 27,28 | Format-Table * Id CommandLine StartExecutionTime Duration -- ----------- ------------------ -------- 27 dir 2/15/2021 5:12:49 PM 00:00:00.0319401 28 Start-Sleep -Seconds 45 2/15/2021 5:12:53 PM 00:00:45.0073792
Die vom Cmdlet Get-History
bereitgestellten IDs unterscheiden sich von den IDs, die von den allgemeinen Verlaufs-Hotkeys der Windows-Konsole (z. B. F7) bereitgestellt werden, da sich ihre Verlaufsverwaltungstechniken unterscheiden.
Standardmäßig speichert die PowerShell die letzten 4.096 Einträge deines Befehlsverlaufs. Wenn du diese Zahl erhöhen oder verringern möchtest, setze die Variable $MaximumHistoryCount
auf die gewünschte Größe. Um diese Änderung dauerhaft zu machen, setze die Variable in deinem PowerShell-Profilskript.
Die bei weitem nützlichste Funktion des PowerShell-Befehlsverlaufs besteht darin, Ad-hoc-Experimente zu überprüfen und in einem Skript festzuhalten, das du dann immer wieder verwenden kannst. Einen Überblick über diesen Prozess (und ein Skript, mit dem du ihn automatisieren kannst) findest du in Rezept 1.22.
1.22 Programm: Skripte aus deinem Sitzungsverlauf erstellen
Nachdem du eine Weile interaktiv an der Kommandozeile experimentiert hast, um eine mehrstufige Aufgabe zu lösen, möchtest du oft die genauen Schritte, mit denen du das Problem schließlich gelöst hast, behalten oder weitergeben. Das Skript lächelt dich aus deinem History-Puffer an, aber leider ist es von vielen weiteren Befehlen umgeben, die du nicht behalten willst.
Hinweis
Ein Beispiel für die Verwendung des Cmdlets Out-GridView
, um dies grafisch umzusetzen, findest du in Rezept 2.4.
Um dieses Problem zu lösen, verwende das Cmdlet Get-History
, um die zuletzt eingegebenen Befehle anzuzeigen. Dann rufst du Copy-History
mit den IDs der Befehle auf, die du behalten willst, wie in Beispiel 1-16 gezeigt.
Beispiel 1-16. Copy-History.ps1
##############################################################################
##
## Copy-History
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Copy selected commands from the history buffer into the clipboard as a script.
.EXAMPLE
PS > Copy-History
Copies the entire contents of the history buffer into the clipboard.
.EXAMPLE
PS > Copy-History -5
Copies the last five commands into the clipboard.
.EXAMPLE
PS > Copy-History 2,5,8,4
Copies commands 2,5,8, and 4.
.EXAMPLE
PS > Copy-History (1..10+5+6)
Copies commands 1 through 10, then 5, then 6, using PowerShell's array
slicing syntax.
#>
[
CmdletBinding
()]
param
(
## The range of history IDs to copy
[
Alias
(
"Id"
)]
[int[]]
$Range
)
Set-StrictMode
-Version
3
$history
=
@()
## If they haven't specified a range, assume it's everything
if
((
-not
$range
)
-or
(
$range
.
Count
-eq
0
))
{
$history
=
@(
Get-History
-Count
(
[Int16]
::
MaxValue
))
}
## If it's a negative number, copy only that many
elseif
((
$range
.
Count
-eq
1
)
-and
(
$range
[
0
]
-lt
0
))
{
$count
=
[Math]
::
Abs
(
$range
[
0
])
$history
=
(
Get-History
-Count
$count
)
}
## Otherwise, go through each history ID in the given range
## and add it to our history list.
else
{
foreach
(
$commandId
in
$range
)
{
if
(
$commandId
-eq
-
1
)
{
$history
+=
Get-History
-Count
1
}
else
{
$history
+=
Get-History
-Id
$commandId
}
}
}
## Finally, export the history to the clipboard.
$history
|
Foreach
-Object
{
$_
.
CommandLine
}
|
clip
.
exe
Weitere Informationen zum Ausführen von Skripten findest du in Rezept 1.2.
1.23 Einen Befehl aus deinem Sitzungsverlauf aufrufen
Lösung
Verwenden Sie das Cmdlet Invoke-History
(oder seinen Alias ihy
), um einen bestimmten Befehl über seine ID
:
Invoke-History ID
Um deinen Verlauf nach einem Befehl zu durchsuchen, der text
:
PS > #text
<Tab>
Um deinen Befehl mit dem Text eines vorherigen Befehls zu füllen, indem du seine ID
:
PS > #ID
<Tab>
Diskussion
Wenn du deine Shell eine Zeit lang geöffnet hast, füllt sich dein Verlaufspuffer schnell mit nützlichen Befehlen. Die in Rezept 1.9 beschriebenen Hotkeys für die Verlaufsverwaltung zeigen eine Möglichkeit, wie du in deinem Verlauf navigieren kannst, aber diese Art der Verlaufsnavigation funktioniert nur für Befehlszeilen, die du in der jeweiligen Sitzung eingegeben hast. Wenn du einen dauerhaften Befehlsverlauf hast (wie in Rezept 1.31 beschrieben), gelten diese Tastenkombinationen nicht.
Das Cmdlet Invoke-History
veranschaulicht das einfachste Beispiel für die Arbeit mit deinem Befehlsverlauf. Wenn du eine bestimmte Verlaufs-ID angibst (die du vielleicht in deiner Eingabeaufforderung angegeben hast), führt der Aufruf von Invoke-History
mit dieser ID diesen Befehl erneut aus. Weitere Informationen zu dieser Technik findest du in Rezept 1.9.
Als Teil der Unterstützung für die Tabulatorvervollständigung bietet die PowerShell dir auch einen einfachen Zugriff auf vorherige Befehle. Wenn du deinem Befehl das Zeichen #
voranstellst, hat die Tabulatorvervollständigung eine von zwei Möglichkeiten:
- ID-Vervollständigung
-
Wenn du eine Zahl eingibst, findet die Tabulatorvervollständigung den Eintrag in deinem Befehlsverlauf mit dieser ID und ersetzt dann deine Befehlszeile durch den Text dieses Verlaufseintrags. Das ist besonders nützlich, wenn du einen früheren Verlaufseintrag geringfügig ändern möchtest, denn
Invoke-History
selbst unterstützt das nicht. - Mustervervollständigung
-
Wenn du etwas anderes eingibst, sucht die Tabulatorvervollständigung nach Einträgen in deinem Befehlsverlauf, die diesen Text enthalten. PowerShell verwendet den Operator
-like
, um deine Befehlseingaben abzugleichen. Du kannst also alle Platzhalterzeichen verwenden, die dieser Operator unterstützt. Weitere Informationen zum Durchsuchen von Text nach Mustern findest du in Rezept 5.7.
Die PowerShell-Registerkartenvervollständigung wird größtenteils durch die vollständig anpassbare Funktion TabExpansion2
gesteuert. Du kannst diese Funktion leicht ändern, um erweiterte Funktionen einzubinden oder auch nur bestimmte Verhaltensweisen an deine persönlichen Vorlieben anzupassen. Weitere Informationen findest du in Rezept 1.18.
1.24 Programm: Formatierte Ausgabe nach einem Muster durchsuchen
Die eingebauten Filterfunktionen der PowerShell sind zwar unglaublich flexibel (z. B. das Cmdlet Where-Object
), aber sie arbeiten in der Regel mit bestimmten Eigenschaften des eingehenden Objekts. Wenn du nach Text in der formatierten Ausgabe des Objekts suchst oder nicht weißt, welche Eigenschaft den gesuchten Text enthält, ist eine einfache textbasierte Filterung manchmal hilfreich.
Um dieses Problem zu lösen, kannst du die Ausgabe in das Cmdlet Out-String
leiten, bevor du sie an das Cmdlet Select-String
weitergibst:
Get-Service
|
Out-String
-Stream
|
Select-String
audio
Oder du verwendest integrierte Aliasnamen:
Get-Service
|
oss
|
sls
audio
In der Skriptform macht Select-TextOutput
(siehe Beispiel 1-17) genau das und lässt dich nach einem Muster in der visuellen Darstellung der Befehlsausgabe suchen.
Beispiel 1-17. Select-TextOutput.ps1
##############################################################################
##
## Select-TextOutput
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Searches the textual output of a command for a pattern.
.EXAMPLE
PS > Get-Service | Select-TextOutput audio
Finds all references to "Audio" in the output of Get-Service
#>
param
(
## The pattern to search for
$Pattern
)
Set-StrictMode
-Version
3
$input
|
Out-String
-Stream
|
Select-String
$pattern
Weitere Informationen zum Ausführen von Skripten findest du in Rezept 1.2.
1.25 Befehlsausgaben interaktiv anzeigen und verarbeiten
Diskussion
Das Cmdlet Out-GridView
ist eines der wenigen PowerShell-Cmdlets, das eine grafische Benutzeroberfläche anzeigt. Während die Cmdlets Where-Object
und Sort-Object
die gängigste Methode zum Sortieren und Filtern von Listen sind, ist das Cmdlet Out-GridView
sehr effektiv bei der wiederholten Verfeinerung, die dir manchmal bei der Entwicklung komplexer Abfragen hilft. Abbildung 1-4 zeigt das Cmdlet Out-GridView
in Aktion.
Out-GridView
kannst du deine Befehlsausgabe vor allem auf zwei Arten filtern: mit einem Schnellfilterausdruck und einem Kriterienfilter.
Schnellfilter sind ziemlich einfach. Wenn du einen Text in das oberste "Filter"-Fenster eingibst, filtert Out-GridView
die Liste so, dass sie nur Einträge enthält, die diesem Text entsprechen. Wenn du diese Textfilterung auf bestimmte Spalten beschränken möchtest, gibst du einfach einen Spaltennamen vor deinem Suchbegriff ein und trennst die beiden mit einem Doppelpunkt. Du kannst auch mehrere Suchbegriffe angeben. In diesem Fall gibt Out-GridView
nur die Zeilen zurück, die mit allen erforderlichen Suchbegriffen übereinstimmen.
Hinweis
Anders als die meisten Filter-Cmdlets in der PowerShell unterstützen die Schnellfilter im Cmdlet Out-GridView
keine Platzhalter oder regulären Ausdrücke. Für diese Art der erweiterten Abfrage kann eine kriterienbasierte Filterung hilfreich sein.
MitKriterienfiltern kann die Filterung des Cmdlets Out-GridView
genau steuern. Um einen Kriterienfilter anzuwenden, klickst du auf die Schaltfläche "Kriterien hinzufügen" und wählst eine Eigenschaft aus, nach der gefiltert werden soll. Out-GridView
fügt eine Zeile unter dem Schnellfilterfeld hinzu und lässt dich eine von mehreren Operationen auswählen, die auf diese Eigenschaft angewendet werden sollen:
-
Weniger als oder gleich
-
Größer als oder gleich
-
Zwischen
-
Entspricht
-
Ist nicht gleich
-
Enthält
-
Enthält nicht
Zusätzlich zu diesen Filteroptionen kannst du auf Out-GridView
auch die Kopfspalten anklicken und neu anordnen, um nach ihnen zu sortieren.
Verarbeitung der Ausgabe
Wenn du deine Befehlsausgabe in Scheiben und Würfel geschnitten hast, kannst du alle Zeilen markieren, die du behalten möchtest, und Strg+C drücken, um sie in die Zwischenablage zu kopieren. Out-GridView
kopiert die Elemente als tabulatorgetrennte Daten in die Zwischenablage, so dass du die Informationen leicht in eine Tabellenkalkulation oder eine andere Datei zur weiteren Bearbeitung einfügen kannst.
Das Cmdlet Out-GridView
unterstützt nicht nur die Ausgabe über die Zwischenablage, sondern auch das Filtern von Objekten, wenn du den Parameter -PassThru
verwendest. Ein Beispiel für diese Full-Fidelity-Filterung findest du in Rezept 2.4.
1.26 Programm: Interaktives Betrachten und Erforschen von Objekten
Wenn du mit unbekannten Objekten in der PowerShell arbeitest, verbringst du einen Großteil deiner Zeit mit den Befehlen Get-Member
und Format-List
- du navigierst durch Eigenschaften, überprüfst Mitglieder und mehr.
Für Ad-hoc-Untersuchungen ist eine grafische Oberfläche oft nützlich.
Um dieses Problem zu lösen, bietet Beispiel 1-18 eine interaktive Baumansicht, mit der du Objekte erkunden und navigieren kannst. So kannst du zum Beispiel die Struktur eines Skripts aus der Sicht der PowerShell untersuchen (den abstrakten Syntaxbaum):
$ps
=
{
Get-Process
-ID
$pid
}.
Ast
Show-Object
$ps
Weitere Informationen zum Parsen und Analysieren der Struktur von PowerShell-Skripten findest du in Rezept 10.10.
Beispiel 1-18. Show-Object.ps1
#############################################################################
##
## Show-Object
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Provides a graphical interface to let you explore and navigate an object.
.EXAMPLE
PS > $ps = { Get-Process -ID $pid }.Ast
PS > Show-Object $ps
#>
param
(
## The object to examine
[
Parameter
(
ValueFromPipeline
=
$true
)]
$InputObject
)
Set-StrictMode
-Version
3
Add-Type
-Assembly
System
.
Windows
.
Forms
## Figure out the variable name to use when displaying the
## object navigation syntax. To do this, we look through all
## of the variables for the one with the same object identifier.
$rootVariableName
=
dir
variable
:
\*
-Exclude
InputObject
,
Args
|
Where-Object
{
$_
.
Value
-and
(
$_
.
Value
.
GetType
()
-eq
$InputObject
.
GetType
())
-and
(
$_
.
Value
.
GetHashCode
()
-eq
$InputObject
.
GetHashCode
())
}
## If we got multiple, pick the first
$rootVariableName
=
$rootVariableName
|
%
Name
|
Select
-First
1
## If we didn't find one, use a default name
if
(
-not
$rootVariableName
)
{
$rootVariableName
=
"InputObject"
}
## A function to add an object to the display tree
function
PopulateNode
(
$node
,
$object
)
{
## If we've been asked to add a NULL object, just return
if
(
-not
$object
)
{
return
}
## If the object is a collection, then we need to add multiple
## children to the node
if
(
[System.Management.Automation.LanguagePrimitives]
::
GetEnumerator
(
$object
))
{
## Some very rare collections don't support indexing (i.e.: $foo[0]).
## In this situation, PowerShell returns the parent object back when you
## try to access the [0] property.
$isOnlyEnumerable
=
$object
.
GetHashCode
()
-eq
$object
[
0
].
GetHashCode
()
## Go through all the items
$count
=
0
foreach
(
$childObjectValue
in
$object
)
{
## Create the new node to add, with the node text of the item and
## value, along with its type
$newChildNode
=
New-Object
Windows
.
Forms
.
TreeNode
$newChildNode
.
Text
=
"
$(
$node
.
Name
)
[$count] = $childObjectValue"
$newChildNode
.
ToolTipText
=
$childObjectValue
.
GetType
()
## Use the node name to keep track of the actual property name
## and syntax to access that property.
## If we can't use the index operator to access children, add
## a special tag that we'll handle specially when displaying
## the node names.
if
(
$isOnlyEnumerable
)
{
$newChildNode
.
Name
=
"@"
}
$newChildNode
.
Name
+=
"[$count]"
$null
=
$node
.
Nodes
.
Add
(
$newChildNode
)
## If this node has children or properties, add a placeholder
## node underneath so that the node shows a '+' sign to be
## expanded.
AddPlaceholderIfRequired
$newChildNode
$childObjectValue
$count
++
}
}
else
{
## If the item was not a collection, then go through its
## properties
foreach
(
$child
in
$object
.
PSObject
.
Properties
)
{
## Figure out the value of the property, along with
## its type.
$childObject
=
$child
.
Value
$childObjectType
=
$null
if
(
$childObject
)
{
$childObjectType
=
$childObject
.
GetType
()
}
## Create the new node to add, with the node text of the item and
## value, along with its type
$childNode
=
New-Object
Windows
.
Forms
.
TreeNode
$childNode
.
Text
=
$child
.
Name
+
" = $childObject"
$childNode
.
ToolTipText
=
$childObjectType
if
(
[Management.Automation.LanguagePrimitives]
::
GetEnumerator
(
$childObject
))
{
$childNode
.
ToolTipText
+=
"[]"
}
$childNode
.
Name
=
$child
.
Name
$null
=
$node
.
Nodes
.
Add
(
$childNode
)
## If this node has children or properties, add a placeholder
## node underneath so that the node shows a '+' sign to be
## expanded.
AddPlaceholderIfRequired
$childNode
$childObject
}
}
}
## A function to add a placeholder if required to a node.
## If there are any properties or children for this object, make a temporary
## node with the text "..." so that the node shows a '+' sign to be
## expanded.
function
AddPlaceholderIfRequired
(
$node
,
$object
)
{
if
(
-not
$object
)
{
return
}
if
(
[System.Management.Automation.LanguagePrimitives]
::
GetEnumerator
(
$object
)
-or
@(
$object
.
PSObject
.
Properties
))
{
$null
=
$node
.
Nodes
.
Add
(
(
New-Object
Windows
.
Forms
.
TreeNode
"..."
)
)
}
}
## A function invoked when a node is selected.
function
OnAfterSelect
{
param
(
$Sender
,
$TreeViewEventArgs
)
## Determine the selected node
$nodeSelected
=
$Sender
.
SelectedNode
## Walk through its parents, creating the virtual
## PowerShell syntax to access this property.
$nodePath
=
GetPathForNode
$nodeSelected
## Now, invoke that PowerShell syntax to retrieve
## the value of the property.
$resultObject
=
Invoke-Expression
$nodePath
$outputPane
.
Text
=
$nodePath
## If we got some output, put the object's member
## information in the text box.
if
(
$resultObject
)
{
$members
=
Get-Member
-InputObject
$resultObject
|
Out-String
$outputPane
.
Text
+=
"
`n
"
+
$members
}
}
## A function invoked when the user is about to expand a node
function
OnBeforeExpand
{
param
(
$Sender
,
$TreeViewCancelEventArgs
)
## Determine the selected node
$selectedNode
=
$TreeViewCancelEventArgs
.
Node
## If it has a child node that is the placeholder, clear
## the placeholder node.
if
(
$selectedNode
.
FirstNode
-and
(
$selectedNode
.
FirstNode
.
Text
-eq
"..."
))
{
$selectedNode
.
Nodes
.
Clear
()
}
else
{
return
}
## Walk through its parents, creating the virtual
## PowerShell syntax to access this property.
$nodePath
=
GetPathForNode
$selectedNode
## Now, invoke that PowerShell syntax to retrieve
## the value of the property.
Invoke-Expression
"
`$
resultObject = $nodePath"
## And populate the node with the result object.
PopulateNode
$selectedNode
$resultObject
}
## A function to handle key presses on the tree view.
## In this case, we capture ^C to copy the path of
## the object property that we're currently viewing.
function
OnTreeViewKeyPress
{
param
(
$Sender
,
$KeyPressEventArgs
)
## [Char] 3 = Control-C
if
(
$KeyPressEventArgs
.
KeyChar
-eq
3
)
{
$KeyPressEventArgs
.
Handled
=
$true
## Get the object path, and set it on the clipboard
$node
=
$Sender
.
SelectedNode
$nodePath
=
GetPathForNode
$node
[System.Windows.Forms.Clipboard]
::
SetText
(
$nodePath
)
$form
.
Close
()
}
elseif
(
[System.Windows.Forms.Control]
::
ModifierKeys
-eq
"Control"
)
{
if
(
$KeyPressEventArgs
.
KeyChar
-eq
'+'
)
{
$SCRIPT:currentFontSize
++
UpdateFonts
$SCRIPT:currentFontSize
$KeyPressEventArgs
.
Handled
=
$true
}
elseif
(
$KeyPressEventArgs
.
KeyChar
-eq
'-'
)
{
$SCRIPT:currentFontSize
--
if
(
$SCRIPT:currentFontSize
-lt
1
)
{
$SCRIPT:currentFontSize
=
1
}
UpdateFonts
$SCRIPT:currentFontSize
$KeyPressEventArgs
.
Handled
=
$true
}
}
}
## A function to handle key presses on the form.
## In this case, we handle Ctrl-Plus and Ctrl-Minus
## to adjust font size.
function
OnKeyUp
{
param
(
$Sender
,
$KeyUpEventArgs
)
if
(
[System.Windows.Forms.Control]
::
ModifierKeys
-eq
"Control"
)
{
if
(
$KeyUpEventArgs
.
KeyCode
-in
'Add'
,
'OemPlus'
)
{
$SCRIPT:currentFontSize
++
UpdateFonts
$SCRIPT:currentFontSize
$KeyUpEventArgs
.
Handled
=
$true
}
elseif
(
$KeyUpEventArgs
.
KeyCode
-in
'Subtract'
,
'OemMinus'
)
{
$SCRIPT:currentFontSize
--
if
(
$SCRIPT:currentFontSize
-lt
1
)
{
$SCRIPT:currentFontSize
=
1
}
UpdateFonts
$SCRIPT:currentFontSize
$KeyUpEventArgs
.
Handled
=
$true
}
elseif
(
$KeyUpEventArgs
.
KeyCode
-eq
'D0'
)
{
$SCRIPT:currentFontSize
=
12
UpdateFonts
$SCRIPT:currentFontSize
$KeyUpEventArgs
.
Handled
=
$true
}
}
}
## A function to handle mouse wheel scrolling.
## In this case, we translate Ctrl-Wheel to zoom.
function
OnMouseWheel
{
param
(
$Sender
,
$MouseEventArgs
)
if
(
(
[System.Windows.Forms.Control]
::
ModifierKeys
-eq
"Control"
)
-and
(
$MouseEventArgs
.
Delta
-ne
0
))
{
$SCRIPT:currentFontSize
+=
(
$MouseEventArgs
.
Delta
/
120
)
if
(
$SCRIPT:currentFontSize
-lt
1
)
{
$SCRIPT:currentFontSize
=
1
}
UpdateFonts
$SCRIPT:currentFontSize
$MouseEventArgs
.
Handled
=
$true
}
}
## A function to walk through the parents of a node,
## creating virtual PowerShell syntax to access this property.
function
GetPathForNode
{
param
(
$Node
)
$nodeElements
=
@()
## Go through all the parents, adding them so that
## $nodeElements is in order.
while
(
$Node
)
{
$nodeElements
=
,
$Node
+
$nodeElements
$Node
=
$Node
.
Parent
}
## Now go through the node elements
$nodePath
=
""
foreach
(
$Node
in
$nodeElements
)
{
$nodeName
=
$Node
.
Name
## If it was a node that PowerShell is able to enumerate
## (but not index), wrap it in the array cast operator.
if
(
$nodeName
.
StartsWith
(
'@'
))
{
$nodeName
=
$nodeName
.
Substring
(
1
)
$nodePath
=
"@("
+
$nodePath
+
")"
}
elseif
(
$nodeName
.
StartsWith
(
'['
))
{
## If it's a child index, we don't need to
## add the dot for property access
}
elseif
(
$nodePath
)
{
## Otherwise, we're accessing a property. Add a dot.
$nodePath
+=
"."
}
## Append the node name to the path
$tempNodePath
=
$nodePath
+
$nodeName
if
(
$nodeName
-notmatch
'^[$\[\]a-zA-Z0-9]+$'
)
{
$nodePath
+=
"'"
+
$nodeName
+
"'"
}
else
{
$nodePath
=
$tempNodePath
}
}
## And return the result
$nodePath
}
function
UpdateFonts
{
param
(
$fontSize
)
$treeView
.
Font
=
New-Object
System
.
Drawing
.
Font
"Consolas"
,
$fontSize
$outputPane
.
Font
=
New-Object
System
.
Drawing
.
Font
"Consolas"
,
$fontSize
}
$SCRIPT:currentFontSize
=
12
## Create the TreeView, which will hold our object navigation
## area.
$treeView
=
New-Object
Windows
.
Forms
.
TreeView
$treeView
.
Dock
=
"Top"
$treeView
.
Height
=
500
$treeView
.
PathSeparator
=
"."
$treeView
.
ShowNodeToolTips
=
$true
$treeView
.
Add_AfterSelect
(
{
OnAfterSelect
@args
}
)
$treeView
.
Add_BeforeExpand
(
{
OnBeforeExpand
@args
}
)
$treeView
.
Add_KeyPress
(
{
OnTreeViewKeyPress
@args
}
)
## Create the output pane, which will hold our object
## member information.
$outputPane
=
New-Object
System
.
Windows
.
Forms
.
TextBox
$outputPane
.
Multiline
=
$true
$outputPane
.
WordWrap
=
$false
$outputPane
.
ScrollBars
=
"Both"
$outputPane
.
Dock
=
"Fill"
## Create the root node, which represents the object
## we are trying to show.
$root
=
New-Object
Windows
.
Forms
.
TreeNode
$root
.
ToolTipText
=
$InputObject
.
GetType
()
$root
.
Text
=
$InputObject
$root
.
Name
=
'$'
+
$rootVariableName
$root
.
Expand
()
$null
=
$treeView
.
Nodes
.
Add
(
$root
)
UpdateFonts
$currentFontSize
## And populate the initial information into the tree
## view.
PopulateNode
$root
$InputObject
## Finally, create the main form and show it.
$form
=
New-Object
Windows
.
Forms
.
Form
$form
.
Text
=
"Browsing "
+
$root
.
Text
$form
.
Width
=
1000
$form
.
Height
=
800
$form
.
Controls
.
Add
(
$outputPane
)
$form
.
Controls
.
Add
(
$treeView
)
$form
.
Add_MouseWheel
(
{
OnMouseWheel
@args
}
)
$treeView
.
Add_KeyUp
(
{
OnKeyUp
@args
}
)
$treeView
.
Select
()
$null
=
$form
.
ShowDialog
()
$form
.
Dispose
()
Weitere Informationen zum Ausführen von Skripten findest du in Rezept 1.2.
1.27 Zeichne eine Abschrift deiner Shell-Sitzung auf
Lösung
Um eine Abschrift deiner Shell-Sitzung aufzuzeichnen, führst du den Befehl Start-Transcript
aus. Er hat einen optionalen Parameter -Path
, der standardmäßig einen Dateinamen angibt, der auf der aktuellen Systemzeit basiert. Standardmäßig legt die PowerShell diese Datei im Verzeichnis Eigene Dateien ab. Um die Aufzeichnung der Mitschrift deines Shell-Systems zu beenden, führe den Befehl Stop-Transcript
aus.
Diskussion
Obwohl das Cmdlet Get-History
hilfreich ist, zeichnet es die während deiner PowerShell-Sitzung erzeugten Ausgaben nicht auf. Dafür kannst du das Cmdlet Start-Transcript
verwenden. Zusätzlich zu dem zuvor beschriebenen Parameter Path
unterstützt das Cmdlet Start-Transcript
auch Parameter, mit denen du steuern kannst, wie PowerShell mit der Ausgabedatei interagiert.
Wenn du keinen -Path
Parameter angibst, generiert PowerShell einen zufälligen Dateinamen für dich. Wenn du diese Datei nach dem Anhalten des Transkripts verarbeiten möchtest, fügt PowerShell diesen Namen als Eigenschaftsname zur Ausgabe von Start-Transcript
oder Stop-Transcript
hinzu:
PS > $myTranscript = Start-Transcript PS > Stop-Transcript Transcript stopped, output file is D:\Lee\PowerShell_transcript... PS > $myTranscript | fl * -force Path : D:\Lee\PowerShell_transcript.LEE-DESKTOP.kg_Vsm_o.20201217195052.txt Length : 104 PS > $myTranscript.Path D:\Lee\PowerShell_transcript.LEE-DESKTOP.kg_Vsm_o.20201217195052.txt
PowerShell Transkripte beginnen mit einem Standard-Dateikopf, der die Zeit, den Benutzer, den Hostnamen und einige andere nützliche Angaben enthält. Wenn du den-IncludeInvocationHeader
Parameter entweder interaktiv oder über eine systemweite Richtlinie angibst, fügt PowerShell auch ein Trennzeichen zwischen den Befehlen ein, um die automatische Analyse zu unterstützen.
********************** PowerShell transcript start Start time: 20201217190500 Username: ubuntu-20-04\lee Machine: ubuntu-20-04 (Unix 4.19.128.0) Host Application: /opt/microsoft/powershell/7/pwsh.dll Process ID: 1925 OS: Linux 4.19.128-microsoft-standard #1 SMP Tue Jun 23 12:58:10 UTC 2020 (...) ********************** ********************** Command start time: 20201217190502 ********************** PS /mnt/c/Users/lee> Get-Process NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName ------ ----- ----- ------ -- -- ----------- 0 0.00 5.26 0.16 984 984 bash 0 0.00 0.53 0.02 1 0 init 0 0.00 0.07 0.00 982 982 init 0 0.00 0.08 0.32 983 982 init 0 0.00 96.52 0.64 1925 984 pwsh 0 0.00 3.25 0.00 1873 …73 rsyslogd ********************** Command start time: 20201217190504 ********************** PS /mnt/c/Users/lee> cat /var/log/powershell.log (...)
Mit der PowerShell kannst du Abschriften nicht nur manuell aufzeichnen, sondern auch eine Systemrichtlinie einrichten, um sie automatisch aufzuzeichnen. Weitere Informationen dazu, wie du das einrichtest, findest du in Rezept 18.2.
1.28 Erweitere deine Shell mit zusätzlichen Befehlen
Lösung
Wenn das Modul Teil des standardmäßigen PowerShell-Modulpfads ist, führst du einfach den gewünschten Befehl aus:
Invoke-NewCommand
Wenn es nicht ist, verwende den Befehl Import-Module
, um Befehle von Drittanbietern in deine PowerShell-Sitzung zu importieren.
So importierst du ein Modul aus einem bestimmten Verzeichnis:
Import-Module c:\path\to\module
So importierst du ein Modul aus einer bestimmten Datei (Modul, Skript oder Assembly):
Import-Module c:\path\to\module\file.ext
Diskussion
PowerShell unterstützt zwei Befehlssätze, die zusätzliche Cmdlets und Provider ermöglichen: *-Module
und *-PsSnapin
. Snapins waren die Pakete für Erweiterungen in Version 1 der PowerShell und werden nur noch selten verwendet. Snapins unterstützten nur kompilierte Erweiterungen und hatten aufwändige Installationsanforderungen.
Mit Version 2 der PowerShell wurden Module eingeführt, die alles unterstützen, was Snapins unterstützen (und mehr), ohne dass die Installation mühsam ist. Allerdings musste man sich bei PowerShell Version 2 auch daran erinnern, welche Module welche Befehle enthielten, und diese Module manuell laden, bevor man sie verwenden konnte. In Windows gibt es jetzt Tausende von Befehlen in Hunderten von Modulen, so dass es nicht mehr ausreicht, sich auf den eigenen Speicher zu verlassen.
Jede neuere Version der PowerShell verbessert die Situation erheblich, indem sie Module automatisch für dich lädt. Intern verwaltet die PowerShell eine Zuordnung von Befehlsnamen zu dem Modul, das sie enthält. Wenn du einen Befehl verwendest (den du mit dem Cmdlet Get-Command
herausfinden kannst), lädt die PowerShell automatisch das entsprechende Modul. Wenn du dieses automatische Ladeverhalten anpassen möchtest, kannst du die Einstellungsvariable $PSModuleAutoLoadingPreference
verwenden.
Wenn die PowerShell ein Modul mit einem bestimmten Namen importiert, durchsucht sie alle Verzeichnisse, die in der Umgebungsvariablen PSModulePath
aufgeführt sind, und sucht nach dem ersten Modul, das die Unterverzeichnisse enthält, die mit dem von dir angegebenen Namen übereinstimmen. Innerhalb dieser Verzeichnisse sucht sie nach dem Modul (*.psd1
, *.psm1
und *.dll
) mit demselben Namen und lädt es.
Wenn du ein Modul auf deinem eigenen System installierst, befindet es sich in der Regel im Verzeichnis PowerShell\Modules in deinem Verzeichnis Eigene Dateien. In der Windows PowerShell lautet dieser Ort WindowsPowerShell\Modules. Damit die PowerShell in einem anderen Verzeichnis nach Modulen sucht, fügst du es zu deiner persönlichen PSModulePath
Umgebungsvariable hinzu, so wie du auch ein Tools-Verzeichnis zu deinem persönlichen Pfad hinzufügen würdest.
Weitere Informationen zur Verwaltung von Systempfaden findest du in Rezept 16.2.
Wenn du ein Modul aus einem Verzeichnis laden willst, das nicht in PSModulePath
enthalten ist, kannst du den gesamten Verzeichnis- und Modulnamen an den Befehl Import-Module
übergeben. Für ein Modul mit dem Namen Test
verwendest du zum Beispiel Import-Module c:\path\to\Test
. Wie beim Laden von Modulen nach Namen sucht die PowerShell in c:\temp\path\to
nach einem Modul(*.psd1, *.psm1, oder *.dll) mit dem Namen Test
und lädt es.
Wenn du die spezifische Moduldatei kennst, die du laden willst, kannst du auch den vollständigen Pfad zu diesem Modul angeben.
Wenn du weitere Befehle finden willst, siehe Rezept 1.29.
1.29 Zusätzliche PowerShell-Skripte und -Module finden und installieren
Lösung
Benutze den Find-Module
Befehl, um interessante Module zu finden:
PS > Find-Module *Cookbook* | Format-List Name : PowerShellCookbook Version : 1.3.6 Type : Module Description : Sample scripts from the PowerShell Cookbook Author : Lee Holmes (...)
Dann benutze Install-Module
um sie zu deinem System hinzuzufügen.
Install-Module
PowerShellCookbook
-Scope
CurrentUser
Ähnlich verhält es sich mit den Befehlen, Find-Script
und Install-Script
, wenn der Artikel als eigenständiges Skript veröffentlicht wurde. Wenn du das noch nicht getan hast, musst du My Documents\PowerShell\Scripts
zu deinem Systempfad hinzufügen. Weitere Informationen zum Ändern des Systempfads findest du in Rezept 16.2.
PS > Find-Script Get-WordCluster | Install-Script -Scope CurrentUser PS > Get-WordCluster -Count 3 "Hello","World","Jello", "Mellow","Jealous","Wordy","Sword" Representative Items -------------- ----- Wordd {World, Wordy, Sword} Jealou {Jello, Jealous} Hellow {Hello, Mellow}
Diskussion
Die PowerShell Gallery ist der weltweite Knotenpunkt für die Veröffentlichung und den Austausch von PowerShell-Skripten und -Modulen. Sie enthält Tausende von Modulen: offizielle Unternehmensversionen von Microsoft und vielen anderen Unternehmen, beliebte Community-Projekte wie das DbaTools-Modul für die SQL-Verwaltung und lustige Skurrilitäten wie OutConsolePicture zur Anzeige von Bildern als ANSI-Grafiken.
Über die Weboberfläche der PowerShell Gallery kannst du suchen, browsen und erforschen, aber das ist natürlich nicht die Art und Weise, wie du sie über diePowerShell nutzt.
In der PowerShell kannst du mit den Befehlen Find-Module
und Install-Module
mit der PowerShell Gallery interagieren und Module aus ihr installieren. Du kannst Module nach Namen, Tags und sogar nach den Fähigkeiten der Rolle "Just Enough Administration" suchen.
Wenn du zum ersten Mal versuchst, ein Modul aus der PowerShell-Galerie zu installieren, gibt die PowerShell eine Warnung aus:
PS > Install-Module someModule -Scope CurrentUser Untrusted repository You are installing the modules from an untrusted repository. If you trust this repository, change its InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from 'PSGallery'? [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"):
Wie bei allen anderen Code-Sharing-Repositories gibt es auch bei der PowerShell-Galerie keine Einschränkungen, wer oder was in der Galerie veröffentlicht werden darf. Wenn ein Modul über die Missbrauchsmeldemechanismen gemeldet und als bösartig oder gegen die Nutzungsbedingungen der Galerie verstoßend eingestuft wird, wird es natürlich entfernt. Aber abgesehen davon solltestdu die Galerie nicht als geprüft, genehmigt oder anderweitig vertrauenswürdig ansehen. Um dies anzuerkennen und die Warnung bei zukünftigen Modulinstallationen zu entfernen, kannst du die PowerShell-Galerie auf deinem Computer als vertrauenswürdig erklären:
Set-PSRepository
-Name
PSGallery
-InstallationPolicy
Trusted
Neben der öffentlichen PowerShell-Galerie kann PowerShell auch mit privaten Galerien (einschließlich Dateifreigaben!) kommunizieren. PowerShell verwendet das NuGet-Protokoll. Weitere Informationen zum Erstellen einer privaten PowerShell Gallery findest du in der PowerShell Gallery Dokumentation.
1.30 Befehle aus angepassten Shells verwenden
Diskussion
Wie in Rezept 1.28 beschrieben, sind PowerShell-Module und Snapins die beiden Möglichkeiten, mit denen Drittanbieter zusätzliche PowerShell-Befehle verbreiten und hinzufügen können. Produkte, die angepasste Versionen der PowerShell-Konsole anbieten, tun dies, indem sie die PowerShell mit einem von drei Parametern starten:
-
-PSConsoleFile
, um eine Konsolendatei zu laden, die eine Liste der zu ladenden Snapins enthält. -
-Command
um einen anfänglichen Startbefehl anzugeben (der dann ein Snapin oder Modul lädt) -
-File
um ein anfängliches Startskript anzugeben (das dann ein Snapin oder Modul lädt)
Unabhängig davon, welche Methode du verwendest, kannst du den resultierenden Satz an geladenen Erweiterungen untersuchen, um zu sehen, welche du in deine anderen PowerShell-Sitzungen importieren kannst.
Erkennen von geladenen Snapins
Der Befehl Get-PsSnapin
gibt alle Snapins zurück, die in der aktuellen Sitzung geladen sind. Er gibt immer den Satz der PowerShell-Kern-Snapins zurück, aber auch alle zusätzlichen Snapins, die von der angepassten Umgebung geladen wurden, werden angezeigt. Wenn zum Beispiel der Name eines Snapins, das du kennst, lautet Product.Feature.Commands
lautet, kannst du es in zukünftige PowerShell-Sitzungen laden, indem du eintippst Add-PsSnapin
Product.Feature.Commands
. Um dies zu automatisieren, füge den Befehl in dein PowerShell-Profil ein.
Wenn du dir nicht sicher bist, welches Snapin du laden sollst, kannst du auch den Befehl Get-Command
verwenden, um herauszufinden, welches Snapin einen bestimmten Befehl definiert:
PS > Get-Command Get-Counter | Select PsSnapin PSSnapIn -------- Microsoft.PowerShell.Diagnostics
Erkennen von geladenen Modulen
Wie der Befehl Get-PsSnapin
gibt auch der Befehl Get-Module
alle in der aktuellen Sitzung geladenen Module zurück. Er gibt alle Module zurück, die du bisher in der Sitzung hinzugefügt hast, aber auch alle zusätzlichen Module, die von der angepassten Umgebung geladen wurden. Wenn der Name eines Moduls, das du kennst, zum Beispiel ProductModule
lautet, kannst du es in zukünftige PowerShell-Sitzungen laden, indem du eintippst Import-Module
ProductModule
. Um dies zu automatisieren, füge den Befehl in dein PowerShell-Profil ein.
Wenn du dir nicht sicher bist, welches Modul du laden sollst, kannst du auch den Befehl Get-Command
verwenden, um herauszufinden, welches Modul einen bestimmten Befehl definiert:
PS > Get-Command Start-BitsTransfer | Select Module Module ------ BitsTransfer
1.31 Status zwischen Sitzungen speichern
Diskussion
PowerShell bietet einen einfachen skriptbasierten Zugriff auf eine Vielzahl von System-, Engine- und anderen Ereignissen. Du kannst dich für die Benachrichtigung über diese Ereignisse registrieren und sogar jedes dieser Ereignisse automatisch verarbeiten. Im folgenden Beispiel melden wir uns für das Ereignis mit dem Namen PowerShell.Exiting
an. PowerShell erzeugt dieses Ereignis, wenn du eine Sitzung schließt.
Du kannst dieses Ereignis nutzen, um deinen Status, deine Variablen und alles andere, was du brauchst, zu speichern und wiederherzustellen. Das Modul PSReadLine
speichert deinen Befehlsverlauf bereits automatisch zwischen den Sitzungen. Zu Demonstrationszwecken können wir eine ähnliche Funktion über das Ereignis PowerShell.Exiting
implementieren. Dazu musst du in deinem Profil einen Aufruf an Enable-HistoryPersistence
platzieren(Beispiel 1-19).
Beispiel 1-19. Enable-HistoryPersistence.ps1
##############################################################################
##
## Enable-HistoryPersistence
##
## From PowerShell Cookbook (O'Reilly)
## by Lee Holmes (http://www.leeholmes.com/guide)
##
##############################################################################
<#
.SYNOPSIS
Reloads any previously saved command history, and registers for the
PowerShell.Exiting engine event to save new history when the shell
exits.
#>
Set-StrictMode
-Version
3
## Load our previous history
$GLOBAL:maximumHistoryCount
=
32767
$historyFile
=
(
Join-Path
(
Split-Path
$profile
)
"commandHistory.clixml"
)
if
(
Test-Path
$historyFile
)
{
Import-CliXml
$historyFile
|
Add-History
}
## Register for the engine shutdown event
$null
=
Register-EngineEvent
-SourceIdentifier
`
(
[System.Management.Automation.PsEngineEvent]
::
Exiting
)
-Action
{
## Save our history
$historyFile
=
(
Join-Path
(
Split-Path
$profile
)
"commandHistory.clixml"
)
$maximumHistoryCount
=
1kb
## Get the previous history items
$oldEntries
=
@()
if
(
Test-Path
$historyFile
)
{
$oldEntries
=
Import-CliXml
$historyFile
-ErrorAction
SilentlyContinue
}
## And merge them with our changes
$currentEntries
=
Get-History
-Count
$maximumHistoryCount
$additions
=
Compare-Object
$oldEntries
$currentEntries
`
-Property
CommandLine
|
Where-Object
{
$_
.
SideIndicator
-eq
"=>"
}
|
Foreach
-Object
{
$_
.
CommandLine
}
$newEntries
=
$currentEntries
|
?
{
$additions
-contains
$_
.
CommandLine
}
## Keep only unique command lines. First sort by CommandLine in
## descending order (so that we keep the newest entries,) and then
## re-sort by StartExecutionTime.
$history
=
@(
$oldEntries
+
$newEntries
)
|
Sort
-Unique
-Descending
CommandLine
|
Sort
StartExecutionTime
## Finally, keep the last 100
Remove-Item
$historyFile
$history
|
Select
-Last
100
|
Export-CliXml
$historyFile
}
Dieses Skript könnte alles Mögliche tun, aber in diesem Beispiel speichern wir unseren Befehlsverlauf und stellen ihn wieder her, wenn wir die PowerShell starten. Warum sollten wir das tun? Nun, mit einem umfangreichen Verlaufspuffer können wir Befehle, die wir zuvor ausgeführt haben, leichter wiederfinden und wiederverwenden. Zwei Beispiele für diese Vorgehensweise findest du in den Rezepten 1.21 und 1.23.
Enable-HistoryPersistence
führt zwei Hauptaktionen durch. Zuerst laden wir unseren gespeicherten Befehlsverlauf (falls vorhanden). Dann registrieren wir eine automatische Aktion, die immer dann ausgeführt wird, wenn die Engine das Ereignis PowerShell.Exiting
erzeugt. Die Aktion selbst ist relativ einfach, aber das Exportieren unseres neuen Verlaufs erfordert ein wenig Fingerspitzengefühl. Wenn du mehrere Sitzungen gleichzeitig geöffnet hast, aktualisiert jede Sitzung die gespeicherte Verlaufsdatei, wenn sie beendet wird. Da wir den von den anderen Shells gespeicherten Verlauf nicht überschreiben wollen, laden wir ihn zunächst von der Festplatte neu und kombinieren ihn mit dem Verlauf der aktuellen Shell.
Sobald wir die kombinierte Liste der Befehlszeilen haben, sortieren wir sie und wählen die eindeutigen Zeilen aus, bevor wir sie wieder in der Datei speichern.
Unter findest du weitere Informationen über die Arbeit mit PowerShell-Engine-Ereignissen, siehe Rezept 31.2.
Get PowerShell Kochbuch, 4. Auflage now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.