PowerShell [Einführungstutorial]

Zuletzt aktualisiert am 25. Dezember 2023 von Lars

Jetzt lerne ich PowerShell! Maus-Schubsen ist Out, PowerShell ist angesagt. Gerade am Anfang sitzt man aber doch gerne wie der sprichwörtliche "Ochs vor dem Berg". Hier hilft dieses Tutorial weiter. 

Einleitung

Grundsätzlich sollte man nicht mit der normalen PowerShell anfangen, sondern "Windows-PowerShell ISE" verwenden. Grund? Damit hat man eine Command-Line-Completion sowie eine ausführlichere Hilfe.

Man ruft "Windows-PowerShell ISE" zum Beispiel auf, indem man auf Start klickt und anfängt "Power" zu tippen.

Windows PowerShell ISE aufrufen
Windows PowerShell ISE Oberfläche unter Windows 10

Je nach verwendeter Version kann die Oberfläche unterschiedlich aussehen.

Teilweise müssen für bestimmte Aufgaben (z.B. Microsoft Exchange) Module nachgeladen werden.

Befehle sind in PowerHell in der Form

Verb-Substantiv [optionale Parameterliste]

aufgebaut.

Das Verb sagt, was der Befehl machen soll und das Substantiv mit was der Befehl das machen soll. Die Parameter bestimmen den Befehl genauer.

Hier Beispiele von Befehlen:

  • Add-Member...
  • Get-Name...
  • Set-Name...
  • Remove-Name...

Es gibt aber auch andere Verben als "Add", "Get", "Set" und "Remove". Durch die Kenntnis der gängigen Verben lassen sich zumindest ansatzweise schon einige Befehle erraten. Häufig wird eine bestimmte Gross-Kleinschreibung angegeben, diese kann jedoch vernachlässigt werden. Vergisst man notwenige Parameter, fragt PowerShell interaktiv nach.

Hilfe zu den Befehlen lässt sich durch die Eingabe der folgenden Befehlsfolge aufrufen:

get-help BEFEHL
PowerShell Beispiel get-help

Einfache PowerShell-Befehle

DIR

dir

"DIR" kommt eigentlich aus den MS-DOS-Zeiten und gibt den aktuellen Verzeichnisinhalt aus. Eigentlich aber kein richtiger PowerShell-Befehl. Funktioniert aber.

PowerShell - dir

Den aktuellen Verzeichnisinhalt erkennst du am Cursor - im Screenshot oben ist das aktuelle Verzeichnis "C:\Users\Lars".

CD

cd <parameter>

Auch "CD" ist irgendwie kein richtiger PowerShell Befehl und kommt aus den MS-Dos-Zeiten. Damit wechselst du das Verzeichnis.

PowerShell cd

Du musst den Zielordner nicht komplett eingeben, du kannst auch die Anfangsbuchstaben tippen und dann die Tabulator-Taste verwenden. Die Anfangsbuchstaben müssen allerdings eindeutig sein. Falls nicht, schlägt dir PowerShell den ersten Fund vor. Wenn du nochmal die Tabulator-Taste drückst den nächsten, und so weiter. Du wirst ausserdem feststellen, dass aus dem Fall im obigen Screenshot ...

cd .\Links

... wird. Das liegt daran, dass "." immer das aktuelle Verzeichnis bezeichnet.

".." bezeichnet das Verzeichnis über dem aktuellen. Und somit kommst du so in das Verzeichnis über dem aktuellen ...

cd ..

Mit ...

cd \

... kommst du in das oberste Verzeichnis.

Get-ChildItem

Dir ist kurz, daher benutze ich diesen Befehl noch gerne. Doch PowerShell hat einen entsprechenden Befehl. Mit ...

Get-ChildItem .

... gibst du ebenfalls den Inhalt des aktuellen Verzeichnisses aus.

Ausgabeformat in PowerShell festlegen - Tabelle oder Liste

An Beispiel des Befehle "GET-VM" werden hier verschiedene mögliche Parameter gezeigt. Die Auswahl von Werten und das Ausgabeformat funktioniert aber auch bei anderen PowerShell-Befehlen.

get-vm

ergibt ...

Powershell - Ein Einführungstutorial

Ausgabeformat Liste mit "fl" festlegen

Mittels "fl" (steht für "format list") lässt sich ein Ergebnis in Listenformat darstellen.

Get-ChildItem . | fl
... macht in diesem Fall aber wenig Sinn.

Ausgabeformat Tabelle mit "ft" festlegen

Mittels "ft" (steht für "format table") lässt sich ein Ergebnis in Tabellenform darstellen. Häufig ist das die Standardform. Man könnte daher beim nächsten Befehl das "ft" auch weglassen und würde die gleiche Ausgabe erhalten.

Get-ChildItem . | ft

Ausgabewerte einschränken

Nehmen wir an, es soll bei der Ausgabe nur der Name angezeigt werden.

Get-ChildItem . | ft name

Die Ausgabe sieht noch etwas arg auseinandergerissen aus. Daher setzen wir hier noch den Parameter "autosize" dazu:

Jetzt wollen wir noch mehr Eigenschaften dazu ausgeben, wissen aber nicht wie diese heissen. Da hilft uns ...

Get-ChildItem . | fl *

... weiter.

Das ist nicht besonders übersichtlich, aber so kannst du herauslesen, wie du zum Beispiel Name und Erstellungszeit ausgeben kannst ...

Get-ChildItem . | ft name, CreationTime

Spiel damit ruhig mal herum. Wenn die Spalten auseinander gerissen sind, dann versuche "- autosize dahinter zu setzen". Also ...

Get-ChildItem . | ft name, CreationTime -AutoSize

Weitere PowerShell-Befehle

Remove-Item

Dieser Befehl löscht alle Dateien in einem Ordner.

Beispiel ...

Remove-Item C:\test –Recurse

... löscht alle Dateien im Ordner C:\test samt dem Ordner selbst ohne Rückfrage.

Copy-Item

Dieser Befehl kopiert Dateien.

Copy-Item <Datei1> <Datei2>

PowerShell - Copy-Item Beispiel

Get-Process

Get-Process zeigt alle Prozesse an, die gerade auf dem Rechner laufen, sowie zusätzliche Informationen dazu an.

Get-Process

Get-Services

Dieser Befehl zeigt alle Dienste an, die auf dem Rechner installiert sind, sowie deren aktueller Status.

Get-Services

Get-EventLog

Dieser Befehl gibt das Ereignisprotokoll aus. Beispiel ...

Get-EventLog -LogName Application

...gibt das Application-Log aus.

Ausgabe eines Befehls im nächsten weiterverarbeiten

Das haben wir oben eigentlich schon so ähnlich bei "ft" und "fl" kennen gelernt. Das Ergebnis eines Befehls wird an einen anderen weitergeleitet und von diesem weiterverarbeitet. Zwischen beiden Befehlen steht der senkrechte Strich, der auch Pipe-Zeichen genannt wird. Beispiel ...

Get-EventLog -LogName Application | Out-Host -paging

Dieses Beispiel funktioniert nicht in der ISE, sondern in der normalen PowerShell. Die lange Liste, die der Befehl "Get-EventLog" ausgibt, wird an "Out-Host" weitergeleitet und seitenweise unterbrochen.

powershell-outhost
Powershell - Seitenweise Ausgabe mit "Out-Host"

Wenn du mit Hyper-V arbeitest, funktioniert die folgende Befehlskette so...

Get-VMsnapshot -VMname * | Remove-VMsnapshot

... dass der erste Befehl alle Snapshots ausgibt und der zweite diese löscht.

Scripting mit PowerShell

Interessant wird das Skripting, also das Aneinanderreihen von mehreren Befehlen in einer Datei. Diese können dann wiederum am Stück ausgeführt werden.

Hierzu wird, am Besten in der ISE, eine entsprechende Datei mit der Endung .ps1 angelegt. Eventuell musst du hierzu in der ISE noch der "Skript Pane" bzw. "Skriptbereich" aktiviert werden.

Powershell - Ein Einführungstutorial
Powershell - Ein Einführungstutorial

Schauen wir uns das folgende Skript an:

$VMname = Read-Host "Geben Sie den Namen der neuen VM ein"
Write-Host "Erstelle neue VM: $VMname"
New-VM -Name $VMname

Mit dem Skript oben haben wir auch Variablen unter PowerShell kennen gelernt. Diesen wird einfach ein $ vorangestellt. Sie können direkt im Text ausgegeben werden. Der erste Befehl liesst den Namen einer VM vom Benutzer interaktiv ein, der zweite gibt den Namen zur Kontrolle aus und der dritte erstellt eine neue VM (ohne zusätzliche Parameter).

Variablen kann man sich als Speicherzelle ähnlich dem Speicher eines Taschenrechners vorstellen. Variablen können Werte zugewiesen werden und man kann den Wert einer Variablen auch abfragen.

Ein Skript, das mit der Endung ps1 gespeichert wird, startet jedoch nicht durch Doppelklick, sondern es muss immer zwangsläufig innerhalb einer PowerShell gestartet werden.

Zeitgesteuertes Ausführen eines PowerShell-Skriptes

Zeitgesteuertes Ausführen eines Programmes erledigt man unter Windows mit dem Programm "Aufgabenplanung".

Da PowerShell-Skripte nie direkt, sondern immer nur innerhalb einer PowerShell ausgeführt werden können, gilt es einiges zu beachten beim zeitgesteuerten Ausführen.

Unter "program/script" trägt man den Pfad zur PowerShell ein.

%SystemRoot%\system32\WindowsPowerShell\v1.0\powershell.exe

Unter "Add arguments (optional)" den Skriptnamen inkl. Pfad mit vorangestelltem "-command", also z.B.

-command "C:\Windows\bat\myscript.ps1"

Kommentare

Zeilenweise Kommentare werden durch ein # eingeleitet. Kommentare werden eingesetzt, um bestimmte Aktionen im Skript zu dokumentieren. Oft aber auch, um einfach eine Anweisung zu deaktivieren.

Schleifen und if-Abfragen in PowerShell

Ich lerne am liebsten anhand von Beispielen. Auf mcseboard.de habe ich ein schönes PowerShell-Beispiel gefunden, was anschaulich Schleifen und if-Abfragen zeigt. Auch hier sieht man wieder eine gewisse Verwandtschaft zu PHP in der PowerShell-Syntax.

Das folgende Skript listet alle Dienste auf ("get-service") und führt darüber eine Schleife aus ("foreach"). Dann prüft es, ob der aktuelle Dienst den Status "Running" hat und färbt den Eintrag entsprechend. Die Ausgabe auf die Konsole erfolgt mit dem Parameter "write-host".

$svcs = get-service | Sort-Object Name
foreach($sv in $svcs)
    {
    if($sv.Status -eq "Running")
        {
        write-host -ForegroundColor Green $sv.Status $sv.Name $sv.DisplayName
        }
    else
        {
        write-host -ForegroundColor Red $sv.Status $sv.Name $sv.DisplayName
        }
    }

Vergleichsoperationen in PowerShell

Im obigen Beispiel haben wir bereits den Vergleichsoperator

-eq

kennen gelernt. Er prüft auf Gleichheit. Welche weiteren Operatoren sind verfügbar?

-ne

ungleich

-lt

kleiner

-le

kleiner oder gleich

-gt

grösser

-ge

grösser oder gleich

-and

und

WMI-Abfragen mit PowerShell

Auch WMI-Abfragen lassen sich relativ leicht mit PowerShell realisieren. Häufig kommt man bereits mit dem folgenden kurzen Befehl an die Seriennummer eines Servers.

Get-WmiObject Win32_BIOS

WMI und PowerShell Praxis-Beispiel. Temperaturüberwachung von HP Proliant-Server

Machen wir noch ein praktisches Beispiel. HP-Server haben jede Menge Temperatursensoren verbaut. Einer dafür eignet sich dazu, die Umgebungstemperatur zu überwachen.

$colItems = get-wmiobject -class "HP_NumericSensor" -namespace "root\hpq" -computername "."

foreach ($objItem in $colItems) 
{
      if ($objItem.Name -eq "Temperature Sensor 1")
      {
        $date = (get-date).ToShortDateString()
        $time = (get-date).ToShortTimeString()

        $temperature = $objItem.CurrentReading
        write-host  "$date $time Umgebungstemperatur: $temperature Grad"
      }      
}

Nur der Sensor mit dem Namen "Temperature Sensor 1" liefert den relevanten Wert, daher wird auch nur dieser abgefragt.

Die Befehle

$date = (get-date).ToShortDateString()
$time = (get-date).ToShortTimeString()

liefern uns Datum und Uhrzeit vernünftig formatiert zurück.

Text-Dateien erzeugen mit PowerShell

Das obige Beispiel "Temperaturüberwachung von HP Proliant-Server" lässt sich noch erweitern. Wir können eine INI-Datei erzeugen, die von einem Monitor-Programm ausgewertet werden kann.

$colItems = get-wmiobject -class "HP_NumericSensor" -namespace "root\hpq" -computername "."

foreach ($objItem in $colItems) 
{
      if ($objItem.Name -eq "Temperature Sensor 1")
      {
        $date = (get-date).ToShortDateString()
        $time = (get-date).ToShortTimeString()

        $temperature = $objItem.CurrentReading
        write-host  "$date $time Umgebungstemperatur: $temperature Grad"
        "[Standard]" | out-file -filepath C:\windows\bat\tempcheck\temperature.ini
        "temperature=$temperature" | out-file -append -filepath  C:\windows\bat\tempcheck\temperature.ini
      }      
}

Mittels Out-File lassen sich Ergebnisse von Befehlen oder Variablen in eine Text-Datei schreiben. Ohne die Option -append, wird die Datei immer wieder neu erstellt.

Interessante PowerShell-Beispiele

Verschlüsselte GMX-Mails via PowerShell versenden:

Falls du automatisiert E-Mails via GMX versenden willst, geht das nur noch verschlüsselt. Früher war das Tool "bat" die erste Wahl, das kann jedoch kein SSL mehr. Mit PowerShell gehts auch, nur braucht es da einige Schritte.

Nehmen wir an, Ihre Absenderadresse ist:

monitoring.kaktus@gmx.de

Ihr Password wäre (geht nicht, also nicht probieren):

kakti!0815

Ihre Zieladresse ist:

ziel@kaktus.de

Mit folgendem Skript versendest du die E-Mail:

$smtpServer = "mail.gmx.net"
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$credentials=new-object system.net.networkcredential("monitoring.kaktus@gmx.de","kakti!0815") 
$smtp.credentials = $credentials.getcredential($smtpserver,"25","basic")
$message = New-Object Net.Mail.MailMessage("monitoring.kaktus@gmx.de","ziel@kaktus.de")
$message.Subject = "Hello from PowerShell"
$message.Body = "Dieses wunderbare Mail stammt von der PowerShell..."
$smtp.Send($message)

Update

Noch besser scheint es mit dem folgenden Mail-Code zu funktionieren

$EmailTo = "empfaenger@meine-email.com"
$EmailFrom = "absender@gmx.ch"
$Subject = "Host: $Server - Lauferk: $Device - Platz wird knapp: $Ratio Prozent"
$Body = "Hallo!`r`n Host: $Server - Lauferk: $Device - Platz wird knapp. $Ratio Prozent." 
$SMTPServer = "mail.gmx.net" 
$SMTPMessage = New-Object System.Net.Mail.MailMessage($EmailFrom,$EmailTo,$Subject,$Body)
$SMTPClient = New-Object Net.Mail.SmtpClient($SmtpServer, 587) 
$SMTPClient.EnableSsl = $true 
$SMTPClient.Credentials = New-Object System.Net.NetworkCredential("monitoring.kaktus@gmx.de","ziel@kaktus.de")
$SMTPClient.Send($SMTPMessage)

Freien Speicherplatz auf lokalen Laufwerken mit PowerShell überprüfen

Eine voll gelaufene Partition ist eines der häufigsten Fehlerursachen. Sinnvoll wäre es, wenn der Support rechtzeitig einen Fehlerhinweis hierzu bekäme. Auch dies können wir mit PowerShell und WMI-Abfragen realisieren:

function Round( $value, [MidpointRounding]$mode = 'AwayFromZero' ) 
{
  [Math]::Round( $value, $mode )
}

function sendmail ($Server, $Device, $Ratio)
{
    $Ratio = $Ratio * 100
    $Ratio = Round $Ratio
    write-host $Server $Device $Ratio
    
    $smtpServer = "mail.gmx.net"
    $smtp = new-object Net.Mail.SmtpClient($smtpServer)
	$credentials=new-object system.net.networkcredential("monitoring.kaktus@gmx.de","kakti!0815") 
	$smtp.credentials = $credentials.getcredential($smtpserver,"25","basic")
	$message = New-Object Net.Mail.MailMessage("monitoring.kaktus@gmx.de","ziel@kaktus.de")
    $message.Subject = "Host: $Server - Lauferk: $Device - Platz wird knapp: $Ratio Prozent"
    $message.Body = "Hallo!`r`n Host: $Server - Lauferk: $Device - Platz wird knapp. $Ratio Prozent.`r`nBitte beachten."
    $smtp.Send($message)
}

Clear-Host

$Computername = "MyHost"
$colItems=Get-WmiObject win32_logicaldisk -ComputerName $Computername -Filter "Drivetype=3" 
write-host $Computername
foreach ($objItem in $colItems) 
{
    
     write-host ---- $objItem.DeviceID ----
     $Size = [decimal]$objItem.size / 1024 / 1024 / 1024
     $Size = Round $Size
     write-host Gesamt: $Size
     $Free = [decimal]$objItem.Freespace / 1024 / 1024 / 1024
     $Free = Round $Free
     write-host Frei: $Free

     $Ratio = $Free/$Size
     write-host Ratio: $Ratio
     if ($Ratio -le 0.1)
     {
         sendmail $Computername $objItem.DeviceID $Ratio

     }
}

Idee / Danke an:

Funktionen in PowerShell

Schauen wir uns zum Verständnis Funktionen in PowerShell näher an:

function Funktion( Werte ) 
{
  Anweisungen
}

PowerShell macht keine Unterschiede zwischen Prozedur und Funktion. Das was wir von anderen Sprachen als Prozedur kennen, ist in PowerShell eine Funktion ohne Rückgabewert.

Rückgabewerte in PowerShell verhalten sich komplett anders als in anderen Sprachen, ich werde hier daher erst mal nur sehr kurz darauf eingehen. Wie in anderen Sprachen gibt es zwar das Schlüsselwort return, es kann aber auch leer sein. Es sagt nur, dass alles, was zuvor ausgegeben wurde, zurückgegeben wird.

Ein kleines Beispiel:

function testfunction 
{
$a = "Hello, World"
$a
$a
return
}

Clear-Host 
$result =  testfunction
Write-Host $result

Da $a zwei mal in der Funktion ausgegeben wird, was aber aufgrund der Kapselung nicht an der Konsole erscheint, wird der Rückgabewert (und die Ausgabe) "Hello, World Hello, World" sein.

WMI mit PowerShell

Zurück zur eigentlichen Funktion:

Mit...

$colItems=Get-WmiObject win32_logicaldisk -ComputerName $Computername -Filter "Drivetype=3" 

...werden Laufwerke angesprochen. Durch die Filter-Option wird der Zugriff auf lokale Laufwerke beschränkt.

Mit der Schleife wird Laufwerk für Laufwerk "durchgescannt. Der Rückgabewert in Bytes wird in Gigabytes umgerechnet. Ist das Verhältnis < 0.1, wird die Funktion aufgerufen, die eine Mail verschickt. Mehr Infos zur WMI-Geschichte gibt es unter Working with WMI providers to PowerShell.

Weitere Beispiele

FAQ Windows PowerShell

Windows PowerShell ISE fehlt

Windows PowerShell ISE ist auf Windows-Servern ein Feature, das nachinstalliert werden muss. Du findest es unter "Rollen und Features hinzufügen".

Powershell - Ein Einführungstutorial

PowerShell-Skripte können nicht ausgeführt werden

Erhältst du die Meldung "Die Datei ... kann nicht geladen werden, da die Ausführung von Skripts auf diesem System deaktiviert ist. Weitere Informationen finden Sie unter 'about_Execution_Policies' (http://go.microsoft.com/fwlink/?LinkID=135170)", so musst du zunächst die Ausführung von PowerShell-Skripten freischalten. Hierzu rufe die PowerShell bzw. die PowerShell-ISE mit administrativen Rechten auf. Anschliessend gibst du den folgenden Befehl ein ...

Set-ExecutionPolicy Unrestricted

Nun ist das System aber offen für nicht signierte und eventuell für bösartige Zwecke geschriebene PowerShell-Skripte.

Weitere Lernmöglichkeiten für PowerShell

Du willst meine Arbeit unterstützen? Dann freue ich mich über eine kleine Spende!

7 Kommentare

  1. Moin,
    ich habe bisher nur die Einleitung gelesen und kann nur sagen: Bitte nochmal auf Rechtschreibfehler/Tippfehler korrigieren. Es ist verständlich, was Sie meinen, aber nicht gut zu lesen.
    Liebe Grüße

    1. Hallo Max(?)

      Danke für die berechtigte Kritik. Habe die Überarbeitung des Artikels ganz nach oben auf die To Do Liste gesetzt. Wird demnächst kommen.

      Gruss

      Lars

  2. sorry, aber ein "Einführungstutorial" sollte für Anfänger verständlich sein. Das ist leider überhaupt nicht der Fall. Schon im ersten Absatz:
    "Grundsätzlich sollte man nicht mit der normalen PowerShell anfangen, sondern die Windows-PowerShell ISE verwenden. "

    Wofür steht ISE? Was sind die Unterschiede?

    Dann geht es weiter mit
    "Die Namensgebung der meisten Befehle besteht aus:
    •Get-Name…
    •Set-Name…
    •Remove-Name…"

    Häh? Was ist eine Namensgebung? Wofür brauche ich die?
    Und so geht es immer weiter...leider.

Kommentar hinterlassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert