Powershell-Basics
(Version: 15. Juli 2021)
1. Grundlagen
Windows PowerShell bietet umfassende Möglichkeiten der Systemverwaltung und -automatisierung
auf der Windows-Plattform. PowerShell ist eine Kommandozeilen- und Skriptumgebung, welche auf dem .NET Framework basiert.
Sie erlaubt die Steuerung und Automatisierung von Windows Server aber auch von Anwendungen bzw. Services wie Active Directory,
Hyper-V, Exchange Server, System Center, Skype for Business, Citrix und VMware ESX Server usw. PowerShell-Kenntnisse sind heute
unerlässlich für Windows-Systemadministratorinnen und -administratoren – sei es für Installationen vor Ort
oder in der Cloud. Seit der Version 5.1 steht mit PowerShell Core parallel dazu eine Cross-Plattform-Variante zur Verfügung,
die auch für Linux und macOS eingesetzt werden kann.
Zur mächtigen Skriptsprache wird Powershell dank der .Net-Bibliothek,
die mittlerweile eine ansehnliche Grösse erreicht hat und ständig am weiterwachsen ist.
Aus diesem Grund lädt Powershell nur die am häufigsten benötigten Teile von .Net in den Arbeitsspeicher.
Benötigt man allerdings eine .Net-Komponente, die standardmässig nicht geladen ist, muss man diese explizit
nachladen (Siehe 1.1.2 Nachladen von .Net-Komponenten). Um beispielsweise eine komfortable GUI-Schnittstellen zu realisieren, muss man die Windows.Forms-Assembly
laden, bevor man dann ein z.B. Formular-Objekt erstellen und mit Schaltflächen, Textboxen und weiteren Dingen bestücken kann.
Vorbereitungen:
Damit Powershell vollständig genutzt werden kann, muss unter Umständen folgendes vorher erledigt werden:
#Anzeigen wer eingeloggt ist. (Interessant bezüglich Berechtigungen.)
WhoAmI
#PowerShell-Version anzeigen
$PSVersionTable
#Nachladen von .Net-Komponenten. Variante 1 → Load-Methode (falls man den "Full Name" der Komponete kennt):
[system.reflection.assembly]::Load("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
[reflection.assembly]::Load("System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")
#Nachladen von .Net-Komponenten, Variante 2 → LoadForm-Methode (falls man den absoluten Pfad zur Komponente kennt):
[reflection.assembly]::LoadFrom("C:/Windows/Microsoft.Net/.../System.Windows.Forms/v4.0_4.0.0.0__b77...89/System.Windows.Forms.dll")
#Nachladen von .Net-Komponenten, Variante 3 → LoadWithPartialName-Methode (falls man nur Teile des "Full Name" der Komponente kennt):
[reflection.assembly]::LoadWithPartialName("Windows.Forms")
#Nachladen von .Net-Komponenten, Variante 4 → Mit CmdLet Add-Type (Funktioniert nicht immer. Benötigt z.T. "Full Name"):
Add-Type –AssemblyName Windows.Forms
Weiterführende Literatur:
In diesem Programmierkurs gelten die folgenden Abmachungen:
- Jedes Programm enthält einen Programmheader mit Titel, Version, Autor, Datum und Kurzbeschrieb.
- Jede Codestelle mit für Drittpersonen nicht sofort und eindeutig erkennbarem Zweck, muss mit einem kurzer und prägnanten Inlinekommentar versehen werden.
- Alle Variablen müssen am Anfang des Skripts deklariert und auch initialisiert werden.
- Umlaute wie ä, ö, ü und auch fremdsprachige Zeichen wie é, è, à etc. sind im Programmcode (in Variablennamen, Kommentaren etc.) zu vermeiden. Stattdessen ae, oe und ue benutzen.
- Der Programmiermodus soll eingeschränkt werden. Dies erreicht man mit:
set-psdebug -strict
set-strictmode -version latest
1.1 Absoluter und relativer Pfad
# Absoluter Pfad
C:\Benutzer\FelixMuster\Dokumente\MeinText.txt
# Relativer Pfad
# (Abhängig vom CurrentWorkingDirectory CWD. Der Punkt . steht für das CWD)
.\MeinText.txt # Falls CWD = C:\Benutzer\FelixMuster\Dokumente
.\Dokumente\MeinText.txt # Falls CWD = C:\Benutzer\FelixMuster
# Relative Pfade verwenden
# (Um bei Funktionen, Dateien etc. mit relativen Pfaden zu arbeiten, empfiehlt es sich,
# den aktuellen Pfad bewusst als Current Working Directory CWD z.B. auf den Skriptordner zu setzen)
# Funktioniert nur innerhalb eines abgespeicherten WPS-Skripts!
[String] $scriptPath = Split-Path $MyInvocation.MyCommand.Path
Set-Location $scriptPath
1.2 Vom CmdTool zum CmdLet
Die seit Microsofts Anfängen verfügbare Befehlszeileneingabe Cmd-Tool hat ausgedient. Sie wird durch das mächtigere Powershell mit den unzähligen CmdLet's ersetzt:
- PowerShell ist ein Framework von Microsoft zur Automatisierung, Konfiguration und Verwaltung von Systemen, bestehend aus einem Kommandozeileninterpreter sowie einer Skriptsprache.
- Cmdlet werden die Befehle in der PowerShell-Umgebung genannt. Der Begriff soll verdeutlichen, dass es sich um sehr kleine, spezielle Befehle handelt.
Im Gegensatz zu herkömmlichen Befehlen sind Cmdlets keine Standalone-Anwendungen, das heisst, sie können nicht ohne die PowerShell ausgeführt werden.
Cmdlets können .NET-Klassen oder PowerShell-Skripte sein und mit wenigen Zeilen Programmcode geschrieben werden.
# Unterschied CMD-TOOL ↔ CMDLET
# cmd-Tools → Tools aus der alten Betriebssystem-Shell cmd bzw. Windows-Eingabeaufforderung)
# Funktionieren auch in der Powershell.
# Bsp.: cd, dir, xcopy etc.
# CmdLet's → Powershell-Tools
# Funktionieren nicht in der alten Betriebssystem-Shell cmd
# Bsp: get-ChildItem, get-Process etc.
Namensaufbau → [VERB]-[NAMEN]
Beispiele: get-process, get-help, start-Process, write-Output usw.
# Beispiele von Cmd-Tools und deren CmdLet-Entsprechungen:
CMDTOOL: echo HALLO
CMDLET: write-host "HALLO"
CMDTOOL: echo %cd%
CMDLET: Get-Location
CMDTOOL: cd c:\windows
CMDLET: Set-Location c:\windows
CMDTOOL: cd %userprofile%
CMDLET: Set-Location ~
CMDTOOL: dir .
CMDLET: Get-Childitem .
CMDTOOL: dir /?
CMDLET: Get-Help Get-Childitem
CMDTOOL: mkdir .\testcmd
CMDLET: New-Item -Name testpowershell -ItemType Directory -Path ~
CMDTOOL: rmdir .\testcmd
CMDLET: Remove-Item -Path ~\testpowershell
1.3 Hilfe erhalten
# Zu beachten: Bei Fehlern in Powershell, die nicht zu erklären oder unauffindbar sind, empfiehlt es sich,
# die Powershell ISE neu zu starten.
# Falls sie ihre Powershell-Skripte aus der Powershell-ISE heraus ausführen, gilt es zu beachten,
# dass Variablen nach beendeter Skriptausführung in der Powershell ISE bestehen bleiben.
# Alle verfügbaren CmdLets anzeigen:
get-command # Zeigt alle Alias/CmdLet/Function an
get-command get* # Zeigt alle Alias/CmdLet/Function mit dem Verb "get" an (* → Wildcard)
get-command *alias* # Zeigt alles an, das die Bezeichnung "alias" im Namen enthält
# Alle möglichen CmdLet-Verben anzeigen:
Get-Verb
# CmdLet-Hilfe anzeigen:
update-help # Lädt die aktuellsten Helpfiles herunter
get-help # Hilfe zum Hilfesystem anzeigen
get-help about* # Zeigt alle Helpfile-Einträge an
get-help about_for # Zeigt Hilfetext zur for-Schleife an
get-help about_Functions # Zeigt Hilfetext zu Funktionen an
get-help about_Parameters # Zeigt Hilfetext zu Parametern an
get-help Write-Eventlog -full # Zeigt kompletten Hilfetext zu Write-Eventlog an
get-help Write-Eventlog -examples # Zeigt Beispiel-Skript zu Write-Eventlog an
# Fallbeispiel get-help:
get-help Get-Process # NAME: Get-Process
# ÜBERSICHT: Gets the processes that are running ...
# SYNTAX: Get-Process [[-Name] ] [-ComputerName ] ...
# BESCHREIBUNG: The Get-Process cmdlet gets the processes on a local or ...
# VEWANDTE LINKS: Debug-Process...
# HINWEISE: Zum Aufrufen der Beispiele...
# Beispiele:
# Die neuen Erkenntnisse anwenden:
get-process notepad # Falls der Prozessname an erster Stelle in der Parameterliste erscheint
get-process -name notepad # Sonst mit Parameterbezeichnung -name
get-process -id 3880 # Oder mit der Prozess-ID (falls bekannt bzw. in diesem Beispiel die 3880)
# Objekte (Objekte werden später im Skript ausführlich behandelt!)
# Von einem Objekt Properties, Methods und Datentyp anzeigen:
[string]$myStr = "Hello" # Deklaration und Initialisierung der ersten Beispielvariablen
[int]$myInt = 133 # Deklaration und Initialisierung der zweiten Beispielvariablen
$myStr | get-member # Zeigt alle Eigenschaften und Methoden eines String-Objektes an
$myInt | get-member # Zeigt alle Eigenschaften und Methoden eines Integer-Objektes an
$myStr.gettype().fullname # Zeigt den genauen Datentyp an → System.String
$myInt.gettype().fullname # Zeigt den genauen Datentyp an → System.Int32
"Hallo" | Get-Member # | → Pipeline/Objekt-Weitergabe: Gibt Ergebnis an einen nächsten Befehl weiter
"Hallo".gettype().fullname # Zeigt den Datentyp an.
1.4 Piping, Redirection und Alias
Piping:
Eine Pipe oder Pipeline (Rohrleitung) ist ein Datenstrom zwischen zwei Prozessen durch einen Puffer mit dem Prinzip First In – First Out (FIFO)
und verknüpft Befehle ohne Umweg über Variablen miteinander.
Das Ergebnis eines Befehls wird also direkt an den nächsten Befehl weitergereicht.
Solche Befehlsketten kann man sich als Fabrikfliessband vorstellen.
Rohdaten werden von verschiedenen Prozessen bearbeitet und am Ende erhält man das fertige Produkt. (EVA-Prinzip)
Durch Pipelines werden Objekte von Prozess zu Prozess weitergereicht.
Erst der letzte Prozess in der Verarbeitungskette wandelt das Objekt in für den Benutzer lesbaren Text um.
Vielfach repräsentiert sogar ein einzelner Befehl eine interne Verarbeitungskette, wie zum Beispiel:
"Get-Help" → "Get-Help | Out-Default" (Out-Default ruft intern Format-Table und danach Out-Host auf)
Beispiel Piping:
Get-Process explorer | Get-Member #Get-Process und Get-Member sind CmdLets. Den Pipe «|» erhält man mit ALTGR + 7.
Get-Process | out-file "myProc.txt" #Hat in diesem Fall den gleichen Effekt die der Redirect >
Beispiel Redirecting:
Redirect nennt man die Umleitung des Outputs vom Standard-Ausgabedevice (WPS-ISE-Konsole) in eine z.B. Textdatei.
get-process > "myProc.txt" #Redirect → Es wird der Output in die neu erstellte Datei myProc.txt geschrieben, falls
#diese Datei bereits besteht, ohne Rückfrage der bereits existierende Inhalt überschrieben.
get-process >> "myProc.txt" #Append → Wenn die Datei bereits existiert, wird der Inhalt am Dateiende angehängt,
#sonst wird die Datei neu erstellt.
Alias
Das Wort Alias bedeutet «Sonst genannt / also known as» bzw. ein Alias ist ein Pseudonym für einen anderen Befehl.
Achtung: Eigene Aliase sind nur temporär vorhanden und verschwinden nach einem Neustart der WPS-ISE!
Set-Alias -Name list -Value get-childitem #get-childitem ist ein CmdLet
Set-Alias list get-childitem # Kurzform
Set-Alias np c:\windows\notepad.exe # Alias erzeugen
Set-Alias no1 .\myNumberOneScript.ps1 # Alias erzeugen
Get-Alias # Aliase anzeigen
∇ AUFGABEN
- Lassen sie sich alle cmd-Let's mit dem Verb-Namen "Get" anzeigen.
- Zeigen sie den Hilfetext zum cmd-let Get-Process an
- Wie heisst der komplette Befehl, um sich den Prozess mit der Prozess-ID 7788 anzeigen zu lassen?
- Erstellen sie auf ihrem Desktop ein Unterverzeichnis mit dem Namen "Muster".
Dieses Verzeichnis befüllen sie mit ein paar Dateien nach Wahl (Texte, Bilder, Audio etc.).
Zwei davon müssen Textdateien mit der Endung ".txt" sein.
Erstellen Sie im Verzeichnis "Muster" ein weiteres Unterverzeichnis mit dem Namen "Texte".
In diesem Verzeichis erstellen sie ein weiteres Textfile mit dem Namen "hallo.txt".
Verlangt ist nun ein WPS-Skript, der ein Listing über alle Textfiles (.txt) ab dem Verzeichnis "Muster" inklusive den darunterliegenden erstellt.
Das Resultat soll in eine Datei mit der Bezeichnung "MusterListing.txt" geschrieben werden.
Der Skript soll ausschliesslich relative Pfade verwenden.
∇ LÖSUNGEN
- get-command get*
- get-help get-process
- get-process -id 7788
-
Get-ChildItem . *.txt -Recurse > MusterListing.txt
Get-ChildItem * -Recurse -Include *.txt >> MusterListing.txt
2. Grundelemente der Powershell-Programmierung
2.1 Ausführungsrichtlinien und Codierungsregeln
# ********** AUSFÜHRUNGSRICHTLINIEN IN DER POWERSHELL-KONSOLE ANPASSEN **********
# Um Scripte im Entwicklungsstadium vorbehaltlos ausführen zu können, empfiehlt es sich,
# im Powershell-Terminal die Powershell-Ausführungsrichtlinien anzupassen.
# GELOCKERTE WPS-SICHERHEITS-BARRIEREN = GELADENE PISTOLE! ......denn sie wissen (nicht), was sie tun...
#
Set-ExecutionPolicy AllSigned # Nur signierte Scripts werden ausgeführt
Set-ExecutionPolicy RemoteSigned # Aus dem Internet heruntergeladene Scripts müssen signiert sein
Set-ExecutionPolicy Unrestricted # Bevorzugt! Alle Scripts werden ausgeführt. Unsignierten Scripts aus dem Internet müssen bestätigt werden
Set-ExecutionPolicy Bypass # Keinerlei Einschränkungen, Warnungen oder Prompts
Set-ExecutionPolicy Undefined # Entfernt eine zugewiesene Richtlinie
# ********** CODIERUNGSREGELN IN POWERSHELL-SKRIPT ERZWINGEN **********
# Eigentlich käme man auch ohne aus. Allerdings zwingt dies zum disziplinierten Coden und wirkt prophylaktisch gegen Programmierfehler.
#
# set-psdebug aktiviert/deaktiviert Debuggingfunktionen, Ablaufverfolgungsebene wird festgelegt bzw. Strict-Modus umgeschaltet
set-psdebug -off # Deaktiviert alle Skript-Debuggingfunktionen
set-psdebug -strict # TBZ! Interpreter meldet, wenn auf eine Variable verwiesen wird, bevor ihr ein Wert zugewiesen wurde
# set-strictmode aktiviert bedeutet Abbruch, falls Codierungsregeln gebrochen werden
set-strictmode -off # Deaktiviert Strict-Modus und set-psdebug -strict
set-strictmode -version latest # Bevorzugt! Verhindert u.a. Verweise auf nicht initialisierte Variablen
# HINWEIS: Set-StrictMode ähnelt dem Strict-Parameter von Set-PSDebug.
# "Set-Strictmode -version 1" entspricht "Set-PSDebug -strict" mit der Ausnahme,
# dass sich Set-PSDebug auf alle Bereiche auswirkt.
# Set-StrictMode wirkt sich nur auf den festgelegten Bereich sowie auf die untergeordneten Bereiche aus.
2.2 Script-Programme starten
Script aus der Powershell-ISE oder aus einem anderen Skript heraus starten:
# Im aktuellen Variablen-Gültigkeitsbereich («.» beachten!)
. "C:\Benutzer\FelixMuster\Dokumente\myScript.ps1"
. .\myScript.ps1
# Im eigenen Variablen-Gültigkeitsbereich («&» beachten!)
& "~\Dokumente\myScript.ps1" # ~ bedeutet Home-Verzeichnis
& .\myScript.ps1
Powershellscripts und CmdLet's aus der Konsole CMD aufrufen:
Konsole «cmd.exe» aufrufen und eine der folgenden Befehlszeilen ausführen:
Powershell.exe -command Get-Process # WPS-CmdLet «Get-Process» ausführen
Powershell.exe -command Set-ExecutionPolicy unrestricted # Policy ändern, wie in der WPS-ISA auch, um eigener Skript ausführen zu dürfen
Powershell.exe -command .\myScript.ps1 # Eigener Skript über relativen Pfad aufrufen
∇ AUFGABEN
- Hilfe anfordern: Zeigen sie sich zu dem CmdLet "get-childitem" die Hilfe und die Beispiele (Examples) an.
- Skript ausführen: Sie erhalten den folgenden Powershell-Skript:
#Titel: Mein erster Powershell-Skript
#Autor: Felix Muster
#Datum: 3.3.2020
set-strictmode -version latest # Codierungsregeln
cls # Konsole leeren
write-host "Hello World!"
- Kopieren sie den folgenden Powershell-Skript in den Skriptbereich der Powershell-ISE. (Oberer, weisser Bereich)
- Speichern sie diesen Skript auf ihrem Home-Verzeichnis unter dem Namen hello.ps1
- Führen sie den Skript innerhalb der Powershell ISE aus. (Unterer blauer Bereich bzw. Powershell-Konsole)
- Wechseln sie in das cmd (Eingabeaufforderung) und starten sie den Skript erneut.
- Notieren sie sich ihre Beobachtungen.
∇ LÖSUNGEN
- Hilfe anfordern:
- get-help get-childitem
- get-help get-childitem -examples
- Skript ausführen:
- In der Powershell ISE das grüne Dreieck im Menübereich anklicken oder die Funktionstaste 5 betätigen.
- CMD > powershell.exe -command .\hello.ps1
2.3 Powershell-Script-Template
Nachdem die PowerShell-ISE als Administrator gestartet wurde, soll der folgende Befehl in der Powershell-Konsole ausgeführt werden:
(Siehe auch 1.2 Ausführungsrichtlinien und Codierungsregeln)
Set-ExecutionPolicy unrestricted
Danach ein neues Powershell-Dokument erstellen und mit folgenden Zeilen füllen bzw. nach Bedarf anpassen:
#******************* «PROGRAMM-TITEL eintragen» *******************************
# VERSION: «Aktuelle Versionsnummer»
# AUTOR: «Ihr Name»
# DATUM: «Aktuelles Datum»
# ZWECK: «Zweckangabe bzw. kurze Programmbeschreibung»
#******************************************************************************
set-strictmode -version latest # Codierungsregeln verschärfen
#******************* Variablen-Deklaration & Initialisierung ******************
[int]$myInt = 0 # Für eine Ganzzahl ...-2,-1,0,1,2...
[double]$myDouble = 0.0 # Für einer Dezimalzahl z.B. 6.25
[string]$myString = "Hello World!" # Für eine Textzeile
[bool]$myBool = $TRUE # Logische Variable $True oder $False
#******************* HIER BEGINNT MEIN PROGRAMM *******************************
cls # Bildschirm (Konsole) löschen
#********** EINGABE **************
$myString = Read-Host "Bitte ihren Namen eingeben"
$myDouble = Read-Host "Bitte ihre letzte Zeugnisnote eingeben"
#********** VERARBEITUNG *********
if($myBool -eq $True) # Beispiel für eine Selektion
{
# Tu etwas
}
else
{
# Tu etwas anderes
}
while($myInt -lt 10) # Beispiel für eine Iteration
{
$myInt = $myInt + 1
}
#********** AUSGABE *************
write-Host "Lieber $myString, deine letzte Zeugnisnote war eine $myDouble. Gratuliere!"
#******************* HIER ENDET MEIN PROGRAMM **********************************
2.4 Input/Output
# Wert mit Read-Host einlesen
$intVar = Read-Host "Bitte eine Zahl eingeben:"
# Ausgabe mit Write-Host
Write-Host "Das Resultat lautet:" $intVar
#Ausgabe mit Out-GridView
Get-Services | Out-GridView
2.5 Datentypen und Array
(Aufzählung nicht abschliessend)
[int] $intVar = 0 #32-Bit-Ganzzahl
[double] $dblVar = 0 #Fliesskommazahl
[char] $chrVar = ' ' #Ein Unicode-16-Bit-Character
[string] $strVar = "" #Textzeile
[bool] $boolVar = $true #Boolean oder logische Variable ($true oder $false)
[DateTime] $datVar = "01.31.1999"
[DateTime] $datVar = Get-Date #Variable mit aktuellem Datum füllen
[Object] $objVar = 0 #Der Stamm einer Objekthierarchie
[int[]] $intArr = 66,77,88,99 #Feldvariable / Array
$intArr[0] =22 #Überschreiben des "ersten" Elements 0
$intArr[1] = 33 #Überschreiben des "zweiten" Elements 1
Set-Variable constWert -Value 100 -option ReadOnly #Unveränderbare Konstante mit Inhalt 100
Ergänzung zum Datentyp Array/Feldvariable
Beispiel: Ziehung bzw. Speicherung der Lottozahlen: 6 Zahlen und Zusatzzahl
# ******** Codebeispiel zu Array ********************************************************
set-strictmode -version latest
cls
[int]$i=0 # Laufvariable
[int[]]$lottoziehung = 0,0,0,0,0,0,0 # Array deklarieren und mit 7 Werten initialisieren
# ******** Eingabe ********
while($i -lt 7) # Es folgen 7 Durchgänge: Von 0 bis 6
{
$lottoziehung[$i] = read-Host "Zahl $i eingeben" # Einzelne Lottozahl einlesen
$i++ # Laufvariable um 1 erhöhen
}
$i=0 # Laufvariable zurücksetzen
# ******** Ausgabe ********
while($i -lt 7) # Es folgen wiederum 7 Durchgänge: Von 0 bis 6
{
write-Host "Zahl $i =" $lottoziehung[$i] # Variante zur Ausgabe von Variablen und Text
$i++ # Laufvariable um 1 erhöhen
}
# ******** So können alle sieben Werte auf einen Schlag ausgegeben werden ********
write-Host "Die Lottoziehung lautet $lottoziehung"
∇ AUFGABE
Zahlen zusammenzählen:
Der Benutzer gibt eine positive, ganze Zahl $x ein. Danach werden die ganzen Zahlen ab 0 bis $x zusammengezählt und das Resultat ausgegeben. Bsp.: Eingabe 6 → 1+2+3+4+5+6=21
∇ LÖSUNG
Zahlen zusammenzählen:
# Autor: ARJ
# Datum: 8.1.2021
set-psdebug -strict #Programmiermodus einschraenken
cls
[int]$x=0 #UserZahl
[int]$i=1 #Schleifenvariable
[int]$r=0 #Resultat
$x = read-host "Pos. Zahl eingeben"
while( $i -le $x)
{
$r = $r + $i
$i = $i + 1
}
write-host "Resultat $r"
2.6 Vergleichsoperatoren und Logische Operatoren
Vergleichsoperatoren
-eq # Equal oder Gleich (==)
-ne # Not-Equal oder Ungleich (!=)
-lt # Lower-than oder Kleiner-als (<)
-le # Lower-Equal oder Kleiner-Gleich (<=)
-gt # Greater-than oder Grösser-als (>)
-ge # Greater-Equal oder Grösser-Gleich (>=)
-like # Zeichenkette mit Wildcards finden. Bsp.: "Arnold" -like "Arn*"
-match #Substring finden. Bsp.: "Arnold" -match "nol"
-contains # Suche in Collections. Bsp.: $w = "Do","Re","Mi" → $w -contains "Mi"
Beispiel zu Vergleichsoperatoren
if ( $x -gt 10) {..} else {..}
Logische Operatoren
-not # Invertierung / NOT / NICHT / Aussageumkehrung
-and # AND oder UND Verknüpfung
-or # OR oder ODER Verknüpfung
-xor # Exklusiv-ODER / XOR Verknüpfung
#Beispiel:
while ( $ResultatNOK -or $Abbrechen) { #Tu irgendwas... }
2.7 Die Selektion inkl. Mehrfachselektion Switch-Case
Selektion → Verzweigung
- Zweiseitige Selektion:
if ( $userInput -ne $null )
{
echo "Input was [$userInput]"
}
else
{
echo "User cancelled the form!"
}
- Entscheidungen können auch mit Pipelines formuliert werden. Die Bedingung ist wahr ($true), wenn die Pipe mindestens ein Objekt zurückliefert:
if (dir *.txt | Select-String "Steuererklaerung")
{
write-host "Es existiert mindestens eine Datei Steuererklaerung"
}
- Mehrfachselektion (Switch-Case):
switch($A)
{
1 { Write-Host "Eins" }
2 { Write-Host "Zwei" }
3 { Write-Host "Drei" }
default { Write-Host "Leer" }
}
∇ AUFGABEN
- Zahleneingabe prüfen:
Der Benutzer gibt eine ganze Zahl zwischen -100 und + 100 ein. Danach wird geprüft, ob die eingegebene Zahl negativ, Null oder positiv ist. Der Befund wird auf der Konsole ausgegeben.
- Verknüpfte Bedingung: Eine Funktion soll nur dann ausgeführt werden, falls Motor 1 nicht eingeschaltet ist, oder Motor 2 nicht eingeschaltet ist,
oder beide ausgeschaltet sind. Sofern noch nicht geschehen, erstellen sie das Struktogramm. Danach schreiben sie das erforderliche Powershell-Programm.
- Wer ist älter: Die Geburtsdaten (Dreiteilig als Geburtsjahr [int], Geburtsmonat [int] und Geburtstag [int]) zweier Personen sind gegeben. Stellen sie fest,
welche von den beiden Personen die ältere ist.
(Hinweis: Das Datum MUSS dreiteilig Tag/Monat/Jahr eingelesen werden. Datumsdatentypen sind nicht erlaubt.)
Sofern noch nicht geschehen, erstellen sie das Struktogramm. Danach schreiben sie das erforderliche Powershell-Programm.
- Schaltjahr bestimmen: Das Programm bzw. der Algorithmus soll bestimmen, ob es sich bei der vom Benutzer eingegebenen Jahreszahl um ein Schaltjahr handelt oder nicht.
Für die Bestimmung eines Schaltjahrs gilt die folgende Regel:
Jedes vierte Jahr ist ein Schaltjahr. Alle hundert Jahre wird diese Regel gebrochen, um kleine Fehler zu korrigieren.
Dabei entsteht wiederum ein winziger Fehler, der alle vierhundert Jahre korrigiert wird,
indem dann ein vermeintlich ausgefallenes Schaltjahr doch wieder eines ist. Im Februar 2000 tritt genau dieser Fall ein.
Tipp: Verwenden Sie den Modulo-Operator %. Der Modulo-Operator gibt jeweils den Rest einer Division an. Bsp. 10 % 4 gibt den Wert 2
Sofern noch nicht geschehen, erstellen sie das Struktogramm. Danach schreiben sie das erforderliche Powershell-Programm.
- Zahlen sortieren:Erstellen Sie das Programm "Zahlen sortieren" anhand des folgenden Struktogramms:
Geben Sie die Zahlenreihe in aufsteigender und absteigender Reihenfolge an.
∇ LÖSUNGEN
- Zahleneingabe prüfen:
# Autor: ARJ
# Datum: 8.1.2021
set-psdebug -strict #Programmiermodus einschraenken
cls #Fenster leeren
[int]$meineZahl=0
$meineZahl = read-host "Bitte Zahl -100 bis +100 eingeben"
if( $meineZahl -lt 0)
{ #Beginn Block
write-host "Zahl negativ"
} # End Block
else
{
if ( $meineZahl -gt 0)
{
write-host "Zahl grösser 0"
}
else
{
write-host "Zahl ist gleich 0"
}
}
- Verknüpfte Bedingung:
#******************* «Verknüpfte Bedingung» ***********************************
# VERSION: V.1
# AUTOR: ARJ
# DATUM: 7.12.2020
# ZWECK: Motoren einschalten (Umgesetzt ist Struktogramm Variante3)
#******************************************************************************
# Auf der Konsole ausführen: Set-ExecutionPolicy unrestricted
set-strictmode -version latest # Codierungsregeln verschärfen
#******************* Variablen-Deklaration & Initialisierung ******************
[string]$meineAntwort = "n" #Hilfsvariable für die Eingabe
[bool]$motor1_ON = $false #Motor-1
[bool]$motor2_ON = $false #Motor-2
#******************* HIER BEGINNT MEIN PROGRAMM *******************************
cls # Bildschirm (Konsole) löschen
#********** EINGABE **************
$meineAntwort = Read-Host "Motor-1 eingeschaltet? (j/n)"
if($meineAntwort -eq "j")
{
$motor1_ON = $true
}
$meineAntwort = Read-Host "Motor-2 eingeschaltet? (j/n)"
if($meineAntwort -eq "j")
{
$motor2_ON = $true
}
#********** VERARBEITUNG *********
if($motor1_ON -and $motor2_ON)
{
write-Host "Funktion nicht ausführen"
}
else
{
write-Host "Funktion ausführen"
}
#******************* HIER ENDET MEIN PROGRAMM **********************************
- Wer ist älter:
# ******** Wer ist aelter ********
# Programm-Versionsnummer: 1.0
# Autor: ARJ
# Datum: 2. 12. 2019
# Beschreibung: Bestimmern, wer von zwei Personen aelter ist
# Programmiermodus einschraenken
set-psdebug -strict
# Variablen-Deklaration/Initialisierung
[int]$A_Jahr = 0 # Jahrgang Person A
[int]$A_Monat = 0 # Geburtsmonat Person A 1=Januar 12=Dezember
[int]$A_Tag = 0 # Geburtstag Person A 0..31
[int]$B_Jahr = 0 # Jahrgang Person B
[int]$B_Monat = 0 # Geburtsmonat Person B 1=Januar 12=Dezember
[int]$B_Tag = 0 # Geburtstag Person B 1..31
cls # ClearScreen
# *** EINGABE Person A ***
$A_Jahr = Read-Host "Erste Person -> Geburtsjahr (4-stellig): "
$A_Monat = Read-Host "Erste Person -> Geburtsmonat (1=Januar ... 12=Dezember): "
$A_Tag = Read-Host "Erste Person -> Geburtstag (1 ... 31): "
# *** EINGABE Person B ***
$B_Jahr = Read-Host "Zweite Person -> Geburtsjahr (4-stellig): "
$B_Monat = Read-Host "Zweite Person -> Geburtsmonat (1=Januar ... 12=Dezember): "
$B_Tag = Read-Host "Zweite Person -> Geburtstag (1 ... 31): "
# *** VERARBEITUNG + Ausgabe ***
if ($A_Jahr -gt $B_Jahr)
{
Write-Host "Die zweite Person ist aelter als die erste!"
}
else
{
if ($A_Jahr -lt $B_Jahr)
{
Write-Host "Die erste Person ist aelter als die zweite!"
}
else
{
if ($A_Monat -gt $B_Monat)
{
Write-Host "Die zweite Person ist aelter als die erste!"
}
else
{
if ($A_Monat -lt $B_Monat)
{
Write-Host "Die erste Person ist aelter als die zweite!"
}
else
{
if ($A_Tag -gt $B_Tag)
{
Write-Host "Die zweite Person ist aelter als die erste!"
}
else
{
if ($A_Tag -lt $B_Tag)
{
Write-Host "Die erste Person ist aelter als die zweite!"
}
else
{
Write-Host "Die erste Person ist gleich alt wie die zweite!"
}
}
}
}
}
}
# ******** Hier endet das Programm ********
- Schaltjahr bestimmen:
# ******** Schaltjahr ********
# Programm-Versionsnummer: 1.0
# Autor: ARJ
# Datum: 2. 12. 2019
# Beschreibung: Schaltjahr bestimmen
# Programmiermodus einschraenken
set-psdebug -strict
# Variablen-Deklaration/Initialisierung
[int]$meinJahr = 0 # Zu ueberpruefendes Jahr
cls # ClearScreen
# *** EINGABE ***
$meinJahr = Read-Host "Geben sie das Jahr an, von welchem sie ueberpruefen wollen, ob es ein Schaltjahr ist: "
# *** VERARBEITUNG + Ausgabe ***
if ( ($meinJahr % 4) -ne 0)
{
Write-Host "Das Jahr" $meinJahr "ist kein Schaltjahr!"
}
else
{
if ( ($meinJahr % 100) -ne 0)
{
Write-Host "Das Jahr" $meinJahr "ist ein Schaltjahr!"
}
else
{
if ( ($meinJahr % 400) -eq 0)
{
Write-Host "Das Jahr" $meinJahr "ist ein Schaltjahr!"
}
else
{
Write-Host "Das Jahr" $meinJahr "ist kein Schaltjahr!"
}
}
}
# ******** Hier endet das Programm ********
- Zahlen sortieren: Naheliegend währe die Idee, das Array mit einem Powershell-Cmd-Let auf diese Art zu sortieren: $arrx = $arrx | sort
Allerdings soll ein eigener Algoritmus entwickelt und implementieret werden.
In der folgenden Musterlösung wird nun aber nicht die Umsetzung gemäss dem Struktogramm in der Aufgabenstellung gezeigt,
dass sollte nun ja kein Problem mehr darstellen, sondern vielmehr eine etwas raffiniertere und alternative Methode: Mit einem sogenannten Bubble-Sort: (Auf die selbe Weise können
fast beliebig lange Zahlenreihen sortiert werden.)
- Die erste Zahl wird mit der zweiten Zahl verglichen und falls nötig vertauscht.
- Die zweite Zahl wird mit der dritten Zahl verglichen und falls nötig vertauscht.
- Um eine allfällig in der Mitte sitzende kleinste Zahl nach links durchzureichen, wird der erste Schritt wiederholt bzw.
die erste Zahl wird mit der zweiten Zahl wiederholt verglichen und falls nötig vertauscht.
- Testen sie den Algorithmus mit verschiedenen Zahlenreihen, wie z.B. 123, 132, 312, 213, 321, 231, 111, 112, 211, 121, 332 etc.
#Titel: Zahlen sortieren
#Autor: ARJ
#Datum: 1.3.2021
set-strictmode -version latest
cls
[int]$i = 0 # Laufvariable
[int]$x = 0 # Hilfsvariable zm Umkopieren
[int[]]$arrx = 0,0,0 # Array mit drei Werten
# ****** Eingabe von drei Zahlen
for($i=0; $i -lt 3; $i++)
{
$arrx[$i] = read-host "Bitte" ($i+1) ". Zahl eingeben"
}
# ****** Verarbeitung
if($arrx[0] -gt $arrx[1]) #Grösseren Wert nach rechts reichen
{
$x = $arrx[1] # Wert zwischenspeicher
$arrx[1] = $arrx[0] # Wert umkopieren
$arrx[0] = $x # Zwischenspeicher zurückschreiben
}
if($arrx[1] -gt $arrx[2]) #Grösseren Wert nach rechts reichen
{
$x = $arrx[2] # Wert zwischenspeicher
$arrx[2] = $arrx[1] # Wert umkopieren
$arrx[1] = $x # Zwischenspeicher zurückschreiben
}
if($arrx[0] -gt $arrx[1]) #Grösseren Wert nach rechts reichen
{
$x = $arrx[1] # Wert zwischenspeicher
$arrx[1] = $arrx[0] # Wert umkopieren
$arrx[0] = $x # Zwischenspeicher zurückschreiben
}
# ****** Ausgabe der sortierten Reihe
write-host $arrx
2.8 Die Iteration → Schleife
# Kopfgesteuerte Iteration:
[int] $myVar = 0
while ($myVar -lt 10)
{
Write-Host Hello
$myVar++
}
# Fussgesteuerte Iteration:
[int] $myVar = 0
do
{
Write-Host "Hello"
$myVar++
} while ($myVar -lt 10)
# Kopfgesteuerte Iteration → Spezialfall for-Schleife:
[string] $sterne = ""
[int] $maxPosition=8
for ( $currPosition = 1 ; $currPosition -le $maxPosition ; $currPosition++ )
{
$sterne = $sterne + "*"
}
write-host "Anzahl" $sterne
# Objektsammlung Version 1:
# Diese Iteration arbeitet eine Sammlung von Objekten ab. Die sogenannte
# Element-Variable $Variable speichert bei jedem Durchgang jeweils ein Objekt
# der Objekt-Gruppe.
Foreach ($Variable in get-childitem C:\windows)
{
...
$Variable.Name
$Variable.CreationTime
...
}
# Objektsammlung Version 2:
# Vereinfachte Variante mit dem CmdLet «Foreach_Object»
# Man kann mit der Standardvariable $_ auf die einzelnen Objekte in der Pipeline zugreifen:
# Vorsicht: Das vorangegangenen Beispiel mit der foreach-Funktion unterscheidet sich zu
# diesem Beispiel darin, dass hier ein foreach-CmdLet verwendet wird.
# Im Gegensatz zur Funktion muss beim CmdLet die geschweifte Klammer «{» auf derselben Zeile folgen.
get-childitem C:\windows | Foreach-Object {
...
$_.Name
$_.CreationTime
...
}
∇ AUFGABEN
- Zeichenkolonne: Der Vorspann des Kinoklassikers The Matrix zeigt wasserfallähnlich unzählige, fallende grüne Buchstaben.
Etwas ähnliches soll in diesem Programm erreicht werden, allerdings beschränkt auf eine einzige Kolonne:
Der Benutzer gibt mit $k ein, wie hoch diese Kolonne sein soll. Die Kolonne soll sich in unserem Beispiel so darstellen:
X, dann 2 mal I, dann wiederholt sich das Ganze bis zur Höhe $k
Bsp:
Eingabe Kolonne $k → 7
Ergibt die folgende Ausgabe: (in der vertikalen Ebene)
X → I → I → X → I → I → X
- Zufallszahl erraten V1: Der Benutzer soll eine vom Computer generierte Zufallszahl (Random Number) zwischen 100 und 999 erraten. Der Computer gibt ihm dazu folgende Hinweise:
- Kalt: Wenn der Benutzer um mehr als 100 daneben liegt
- Warm: Wenn der Benutzer zwischen 100 und 51 daneben liegt
- Heiss: Wenn der Benutzer bis zu 50 daneben liegt
- Bingo: Bei einem Treffer
Zusatzaufgabe: Das Spiel soll, wenn vom Benutzer gewünscht, wiederholt werden. Ausserdem soll das Ratespiel zu jedem Zeitpunkt vom Benutzer abgebrochen werden können.
Sofern noch nicht geschehen, erstellen sie das Struktogramm. Danach schreiben sie das erforderliche Powershell-Programm.
- Lichtkubus: Es soll ein dreidimensionales Feld entsprechend eingefärbt werden. Hinweis zum Koordinatensystem: Die unterste Kugel links hat die Position x=0, y=0, z=0.
Sofern noch nicht geschehen, erstellen sie das Struktogramm. Danach schreiben sie das erforderliche Powershell-Programm für diese konkrete Lichtansteuerung.
- Zufallszahl erraten V2: Der Computer würfelt eine Zahl zwischen 1 und 100, die der User anschliessend erraten soll.
Jeder Tipp wird mit der Meldung "Zahl ist grösser" ode "Zahl ist kleiner" quittiert. Nach 10 erfolglosen Rateversuche kann der User entscheiden, ob er ein neues Spiel wünscht.
Sofern noch nicht geschehen, erstellen sie das Struktogramm. Danach schreiben sie das erforderliche Powershell-Programm.
- Fibonacci-Folge: Die Fibonacci-Folge ist die unendliche Folge von natürlichen Zahlen, die ursprünglich mit zweimal der Zahl 1 beginnt
oder in der modernen Schreibweise zusätzlich mit einer führenden Zahl 0 versehen ist.
Im Anschluss ergibt jeweils die Summe zweier aufeinanderfolgender Zahlen die unmittelbar danach folgende Zahl:
Realisieren sie nun das entsprechende Programm wie folgt:
- Der Benutzer gibt an, wie viele Fibonacci-Zahlen berechnet bzw. angezeigt werden sollen.
- Die Berechnung der Zahlen soll mit einer Iteration gelöst werden.
- Tipp: Zur Berechnung ein Array verwenden. Dabei aber eine allfällige Array-Bereichsüberschreitung beachten.
- Tannenbaum zeichnen: Der Benutzer gibt als Zahl ein, wie hoch der Tannenbaum (Anzahl Zeilen ab 1 bis 10) werden soll.
Erstellen bzw. "zeichnen" sie anschliessend diesen Tannenbaum. Benutzen sie im Programmcode dafür ausschliesslich die Iteration "for()".
Wie die for-Schleife angewendet wird, erklärt ihnen der Fachbeitrag zu Powershell.
- Taschenrechner:
- Erstellen Sie einen einfachen Rechner mit den folgenden mathematischen Grundoperationen: + , - , * , / , % (% ist die Module oder Restwertfunktion)
- Die Benutzereingabe soll nach UPN (Umgekehrte polnische Notation, wie sie bei HP-Taschenrechner Verwendung findet/fand) erfolgen: 1. Zahl , 2. Zahl, Operand
- Nachdem das Resultat am Bildschirm erschienen ist, hat der User die Wahl, mit dem Buchstaben "X" (für Exit) das Programm zu verlassen oder mit einer weiteren Zahl und Operand die Rechnung fortzusetzen.
- Die Berechnungen sollen mit doppelter Genauigkeit durchgeführt werden.
- Die Resultatsausgabe soll mathematisch gerundet auf vier Nachkommastellen angezeigt werden.
- Lauzeitfehler wie z.B die Division durch Null sollen abgefangen und entsprechend signalisiert werden.
∇ LÖSUNGEN
- Zeichenkolonne:
# Autor: ARJ
# Datum: 8.1.2021
set-psdebug -strict #Programmiermodus einschraenken
cls
[int]$k=0
[int]$i=0
$k = read-host "Höhe eingeben"
while ($i -lt $k)
{
if( $i % 3 -eq 0)
{
write-host "X"
}
else
{
write-host "I"
}
$i = $i + 1
}
- Zufallszahl erraten V1:
# ******** Zahl wuerfeln ********
# Programm-Versionsnummer: 1.0
# Autor: ARJ
# Datum: 2. 12. 2019
# Beschreibung: Computer wuerfelt Zufallszahl und User soll sie anschliessend erraten
# Programmiermodus einschraenken
set-psdebug -strict
# Variablen-Deklaration/Initialisierung
[int]$meineZufallszahl = 0 # Vom Computer zufaellig gewaehlte Zahl
[int]$meinRateversuch = 0 # Vom User ausgedachte Zahl
[uint]$meineDifferenz = 0 # Differenzbetrag mit Datentyp Unsigned INT -> Vorzeichenloser INT
[boolean]$ZahlGefunden = $false #Flag fuer Treffer
[boolean]$abbrechen = $false #Flag fuer Programmabbruch
cls # ClearScreen
# *** Vorarbeiten ***
$meineZufallszahl = Get-Random -Minimum 100 -Maximum 999
# *** VERARBEITUNG ***
while ( (-not $ZahlGefunden) -and (-not $abbrechen) )
{
Write-Host "Nur fuer Schummler: Der Computer hat" $meineZufallszahl "gewuerfelt ;-)"
$meinRateversuch = Read-Host "Welche Zahl hat sich der Computer ausgedacht? (Mit Zahl 0 das Programm abbrechen!) "
if ( $meinRateversuch -eq 0 )
{
$abbrechen = $true
}
else
{
$meineDifferenz = [Math]::abs($meinRateversuch - $meineZufallszahl)
# Hinweis: [Math]::abs -> Absolut- oder Betragswert und somit immer positiv!
if ( $meineDifferenz -gt 100 )
{
Write-Host "KALT! (Mehr als 100 davon entfernt)" -ForegroundColor Blue -Background white
}
else
{
if ( $meineDifferenz -gt 50 )
{
Write-Host "WARM! (51 bis 100 davon entfernt)" -ForegroundColor green
}
else
{
if ( $meineDifferenz -eq 0)
{
Write-Host "--- BINGO! --- (Zahl gefunden)" -ForegroundColor black -Background white
$ZahlGefunden = $true
}
else
{
Write-Host "HEISS! (50 und weniger davon entfernt)" -ForegroundColor red
}
}
}
}
}
# Meldung bei Abbruch
if ( $abbrechen )
{
Write-Host "Sie haben das Programm abgebrochen!"
}
# ******** Hier endet das Programm ********
- Lichtkubus:
# ******** Lichtkubus **********************************
# Programm-Versionsnummer: 1.0
# Autor: ARJ
# Datum: 8. Dez. 2020
# Beschreibung: Rote und cyanfarbene Kugeln zählen
# Auf der Konsole ausführen: Set-ExecutionPolicy unrestricted
# Programmiermodus einschraenken
set-psdebug -strict
# Variablen-Deklaration/Initialisierung
[int]$posX = 0 # Koordinate der Kugel in x-Richtung
[int]$posY = 0 # Koordinate der Kugel in y-Richtung
[int]$posZ = 0 # Koordinate der Kugel in z-Richtung
[int]$AnzKugelnCyan = 0 # Anzahl Kugeln mit der Farbe Cyan
[int]$AnzKugelnRot = 0 # Anzahl Kugeln mit der Farbe Rot
cls # ClearScreen
# Eingabe-Verarbeitung-Ausgabe
while ($posY -lt 7)
{
while ($posZ -lt 7)
{
while ($posX -lt 7)
{
if ( $posY -eq 3)
{
$AnzKugelnRot = $AnzKugelnRot + 1
}
else
{
if ( $posX -eq 3)
{
$AnzKugelnRot = $AnzKugelnRot + 1
}
else
{
$AnzKugelnCyan = $AnzKugelnCyan + 1
}
}
$posX = $posX + 1
}
$posX = 0
$posZ = $posZ + 1
}
$posZ = 0
$posY = $posY + 1
}
write-host "Von insgesamt 343 Kugeln sind $AnzKugelnCyan cyan-farben und $AnzKugelnRot rot"
# ******** Hier endet das Programm ********
- Zufallszahl erraten V2:
# ******** Zahl zwischen 1 und 100 erraten ********
# Programm-Versionsnummer: 1.0
# Autor: ARJ
# Datum: 8. Dez. 2020
# Beschreibung: Computer wuerfelt Zufallszahl und User soll sie anschliessend erraten
# Auf der Konsole ausführen: Set-ExecutionPolicy unrestricted
# Programmiermodus einschraenken
set-psdebug -strict
# Variablen-Deklaration/Initialisierung
[int]$ZufallsZahl = 0 # Vom Computer zufaellig gewaehlte Zahl
[int]$SpielerZahl = 0 # Vom User ausgedachte Zahl
[int]$AnzVersuche = 0 #Anzahl Rateversuche (max. 10)
[char]$Weiterspielen = "n"
cls # ClearScreen
# Eingabe-Verarbeitung-Ausgabe
do
{
$AnzVersuche = 1
$ZufallsZahl = Get-Random -Minimum 1 -Maximum 100
Write-Host "Nur fuer Schummler: Der Computer hat" $ZufallsZahl "gewuerfelt ;-)" #Debugging
do
{
do
{
$SpielerZahl = Read-Host "$AnzVersuche. Versuch: Zahl zwischen 1 und 100 eingeben"
} while ( ($SpielerZahl -lt 1) -or ($SpielerZahl -gt 100) ) # Eingabe zwischen 1 und 100 erzwingen
if($ZufallsZahl -gt $SpielerZahl)
{
Write-Host "Zufallszahl ist groesser"
}
else
{
if($ZufallsZahl -lt $SpielerZahl)
{
Write-Host "Zufallszahl ist kleiner"
}
else
{
Write-Host "Sie haben die Zahl $ZufallsZahl in $AnzVersuche. Versuchen erraten"
$AnzVersuche = 10 #Damit man die innere Schleife verlässt
}
}
$AnzVersuche = $AnzVersuche + 1
} while ($AnzVersuche -lt 10)
$Weiterspielen = Read-Host "Weiterspielen? (j oder n)"
} while ($Weiterspielen -eq "j")
Write-Host "Sie haben das Programm verlassen."
# ******** Hier endet das Programm ********
- Fibonacci-Folge Variante-A: Mit Hilfsvariablen (Schlepper): (Vorteil dieser Variante: Ohne Array und damit der Gefahr von Array-Bereichsüberschreitung.
Dafür müssen die beiden vorangegangenen Berechnungen zwischengespeichert werden.)
# ******** TITEL: Fibonacci-Folge ********
# Version: V1.0
# Autor: ARJ
# Datum: 29.1.2020
# Beschreibung: Fibonacci-Folge mit Iteration ohne Array
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration & Initialisierung
[int]$fff = 0 # Fibonacci-Zahl, jeweils zwei Berechnungen vorher. Der Anfangswert ist 0
[int]$ff = 1 # Fibonacci-Zahl, jeweils eine Berechnungen vorher. Der Anfangswert ist 1
[int]$f = 1 # Aktuelle Fibonacci-Zahl
[int]$i = 0 # Benutzereingabe Anzahl berechneter bzw. angezeigter Fibonacci-Zahlen
cls # Bildschirm loeschen
do # Eingabe und Ueberpruefung damit Wertebereich nicht ueberlaufen wird (Data-Overflow bei Integer)
{
# Eingabe: Achtung der 47-Wert (0..46) uebersteigt den Wertebereich von Integer
$i = read-host "Anzahl Fibonacci-Zahlen ab 0 (3 bis 47)"
}while ( ($i -lt 3) -or ($i -gt 47) )
# Ausgabe der ersten beiden Fibonacci-Zahlen
# Hinweis: Die Option -NoNewLine verhindert den Zeilenumbruch bei write-host
write-host "Die ersten $i Werte der Fibonacci-Folge lauten:"
write-host -NoNewline "0 "
write-host -NoNewline "1 "
# Ab hier wird berechnet
while($i -gt 2)
{
$f = $fff + $ff
write-host -NoNewline "$f "
$fff = $ff # Letzter Wert in die Variable des vorletzten Wert kopieren
$ff = $f # Aktueller Wert in die Variable des letzten Wert kopieren
$i-- # Zaehlervariable um 1 herunterzaehlen $i = $i -1
}
#Programmende
Fibonacci-Folge Variante-B Mit Array: (Vorteil dieser Variante: Alle Fibonacci-Zahlen stehen jederzeit zur Verfügung.)
# ******** TITEL: Fibonacci-Folge ********
# Version: V1.0
# Autor: ARJ
# Datum: 29.1.2020
# Beschreibung: Fibonacci-Folge mit Iteration und Array
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration & Initialisierung
[int[]]$fibonacciFolge = 0,1,0,0,0,0,0,0,0,0,0 # 11Elemente 0..10 Ersten beiden Zahlen (0,1) MUESSEN gegeben sein
[int]$i = 0
[int]$z = 0
cls # Bildschirm loeschen
do # Eingabe und Ueberpruefung damit Array-Index im gueltigen Bereich ist
{
$z = read-host "Anzahl Fibonacci-Zahlen (3 bis 10)" # Eingabe
}while ( ($z -lt 3) -or ($z -gt 10) )
write-host "0 Zahl = 0 (OPTIONAL)" # Ausgabe der ersten beiden nicht berechneten Werte
write-host "1 Zahl = 1"
for($i=2 ; $i -le $z ; $i++) # Berechnung und Ausgabe der folgenden Fibonacci-Zahlen
{
$fibonacciFolge[$i]=$fibonacciFolge[$i-1] + $fibonacciFolge[$i-2]
write-host $i Zahl "=" $fibonacciFolge[$i]
}
#Programmende
- Tannenbaum zeichnen: Wie das folgende Bild zeigt, sind bei max. 10 Zeilen in der ersten Zeile die ersten neun Zeichen Leerzeichen, gefolgt von einem ersten *.
Pro Zeile reduziert sich der Leerzeichenteil um 1 und der *-Teil erhöht sich um 2.
# ******** TITEL: X-MAS ********
# Version: 1.0
# Autor: ARJ
# Datum: 10. Januar 2020
# Beschreibung: Eigenen Weihnachtsbaum erstellen
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration inklusive Initialisierung (Mit Kommentar zum Verwendungszweck!)
[int]$baumhoehe = 0 # Anzahl Zeilen
[int]$linkeSeite = 9 # Leerzeichen in Zeile bis Tanne *
[int]$baumBreite = 1 # Baumbreite in der entsprechenden Zeile
[int]$i = 0; # Laufvariable
[int]$j = 0; # Laufvariable
[string]$tannenZeile = ''
# *** EINGABE ***
do
{
cls
$baumhoehe = Read-Host "Gewuenschte Baumhoehe eingeben [1..10]"
} while ( ($baumhoehe -lt 1) -or ($baumhoehe -gt 10) )
write-host " " # Leerzeile
for ($j = $baumhoehe ; $j -gt 0 ; $j = $j-1)
{
for ($i = $linkeSeite ; $i -gt 0 ; $i = $i-1)
{
$tannenZeile = $tannenZeile + " "
}
for ($i = $baumBreite ; $i -gt 0 ; $i = $i-1)
{
$tannenZeile = $tannenZeile + "*"
}
Write-Host $tannenZeile
$tannenZeile =''
$linkeSeite = $linkeSeite - 1
$baumBreite = $baumBreite + 2
}
Write-Host " *" # Und noch der Baumstamm ;-)
write-host " " # Leerzeile
Write-Host "MERRY X-MAS AND A HAPPY NEW YEAR!"
write-host " " # Leerzeile
# ******** Hier endet das Programm ********
- Taschenrechner:
# ******** TITEL: UPN-Taschenrechner ********
# Version: 1.0
# Autor: ARJ
# Datum: 8. Januar 2020
# Beschreibung: Musterloesung UPN-Taschenrechner
# Bei UPN werden zuerst die beiden Argumente (Zahlenwerte) eingegeben
# und danach erst der Rechnungsoperator.
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration inklusive Initialisierung
[double]$meinResultat = 0.0 # Eingabe einer ersten Zahl durch den Benutzer und Resultat der Berechnung
[string]$zweiteEingabe = 0.0 # Eingabe einer zweite Zahl durch den Benutzer als Textzeile
[double]$zweiteZahl = 0.0 # Zweite Zahl als wirkliche Zahl
[bool]$zweiteEingabeistZahl = $false # Zur Ueberpruefung ob es sich bei der Eingabe um eine Zahl handelt
[char]$operator = 'n' # Operator +,-;*,/,% oder x fuer Exit.
[bool]$operatorOK = $false # Falscher Operator eingegeben
[bool]$exitOK = $false # Keine weiteren Berechnungen machen
cls #Bildschirm loeschen
#Aeussere Schleife (Weitere Berechnung)
# Erste Zahl eingeben
$meinResultat = Read-Host "Erster Wert eingeben"
do
{
# Zweite Zahl eingeben
$zweiteEingabe = Read-Host "Zweiter Wert eingeben (oder x fuer Exit)"
try
{
0 + $zweiteEingabe | Out-Null #Versucht mit der Variable $zweiteEingabe zu rechnen
$zweiteEingabeistZahl = $true #Hat funktioniert
$zweiteZahl = [double]$zweiteEingabe
}
catch
{
$zweiteEingabeistZahl = $false #Hat nicht funktioniert und somit ist $zweiteEingabe keine Zahl
}
if ( -not $zweiteEingabeistZahl )
{
$exitOK = $true
}
else
{
do
{
#Operator eingeben
$operator = Read-Host "Operator + , - , * , / , % eingeben (oder beliebieger Buchstabe fuer Abbruch)"
#Operator untersuchen und Berechnung ausfuehren
switch ( $operator )
{
'+' { # Plus-Operator gewaehlt
$meinResultat = $meinResultat + $zweiteZahl
write-host "Zwischenresultat" $meinResultat
$operatorOK = $true
}
'-' { # Minus-Operator gewaehlt
$meinResultat = $meinResultat - $zweiteZahl
write-host "Zwischenresultat" $meinResultat
$operatorOK = $true
}
'*' { # Multiplikator-Operator gewaehlt
$meinResultat = $meinResultat * $zweiteZahl
write-host "Zwischenresultat" $meinResultat
$operatorOK = $true
}
'/' { # Divisions-Operator gewaehlt
# Division durch 0 abfangen
if ( $zweiteZahl -ne 0.0 )
{
$meinResultat = $meinResultat + $zweiteZahl
write-host "Zwischenresultat" $meinResultat
$operatorOK = $true
}
else
{ # Operationszeichen neu eingeben lassen
write-host "Division durch 0 ist verboten!"
$operatorOK = $false
}
}
'%' { # Module-Operator (Restwertfunktion) gewaehlt
$meinResultat = $meinResultat + $zweiteZahl
write-host "Zwischenresultat" $meinResultat
$operatorOK = $true
}
'x' { # x fuer Exit gewaehlt
$operatorOK = $true
$exitOK = $true
}
default { # Ungueltiges Operationszeichen bzw. Abbruch
write-host "Ungueltiges Operationszeichen!"
$operatorOK = $false
}
} # End-of-switch
} while ( -not $operatorOK )
}
} while ( -not $exitOK )
write-host "Sie haben die Berechnung abgebrochen!"
# ******** Hier endet das Programm ********
2.9 Try and catch
Da gewisse Anweisungen einen Laufzeitfehler auslösen können, bedient man sich um diese abzufangen einer Try-and-Catch-Struktur: Wird innerhalb des Try-Blocks ein Laufzeitfehler ausgelöst, wird der Catch-Block ausgeführt. Damit kann man eine robuste Dateneingabe erreichen, den Aufruf eines nicht vorhandenen Dienstes abblocken oder Zahlen überprüfen.
- Try-Catch in einer Iteration:
# Variablendeklaration und Initialisierung
[string]$theNumber = "" # Einzugebende und zu prüfende Zahl als Text
[int]$myNumber = 0 # Meine geprüfte Zahl
[bool]$numOK = $False # Hilfsvariable, dient als Flag (Flagge-hoch oder Flagge-tief)
do
{
$theNumber = Read-host "Bitte eine Zahl zwischen 0 und 100 eingeben"
try # Versuche mit der Variablen zu rechnen
{
0 + $theNumber | Out-Null # Rechnung und Resultat dabei verwerfen
$numOk = $True # Boolean-Flag-Variable auf $True → ist eine Zahl
$myNumber = [int]$theNumber # Zahl mit Typecast umkopieren
} # end try-Part
catch # Mit der Variablen kann man offenbar nicht rechnen → Fehler abfangen mit Catch
{
$numOK = $False # Boolean-Flag-Variable auf $False → ist keine Zahl
} # end catch-Part
} until ( ($myNumber -ge 1 -and $myNumber -lt 100) -and $numOK )
write-host "Sie haben die Zahl $myNumber eingegeben"
- Dienst auf sein Vorhandensein überprüfen:
$servicename = Read-host "Bitte geben Sie ein Servicename ein"
try
{ # der Fehler beim ausgeführten Cmdlet wird abgefangen!
Get-Service $servicename -ErrorAction Stop
} # end try-Part
catch
{
Write-Warning "Diesen Service ($servicename) gibt es nicht!"
} # end catch-Part
- Robuste Zahleneingabe (Funktion):
function isNumeric ($x)
{
try
{
0 + $x | Out-Null # Versucht mit der Variable zu rechnen
return $true # Wenn möglich $true zurückgeben
}
catch
{
return $false #Wenn Fehler auftritt, handelt es sich nicht um eine Zahl, somit $false
}
}
∇ AUFGABE
Robuste Benutzereingabe:" Der Benutzer soll eine Zahl von 0 bis 10 eingeben. Anschliessend prüfen sie die Eingabe:
- Wurde anstelle einer Zahl ein Text wie z.B. "zwei" eingegeben? Wenn zutreffend, Ausgabe der Fehlermeldung "Bitte keine Texteingabe!"
- Wurde nur die ENTER-Taste gedrückt? Dann soll die Fehlermeldung "Keine Eingabe gemacht!" angezeigt werden.
- Wurde eine Zahl von 0 bis 10 eingegeben? Wenn nicht: Fehlermeldung "Ihre Zahl ist zu hoch!" oder "Ihre Zahl ist zu tief!"
- War die Eingabe korrekt, geben sie die eingegebene Zahl wie folgt aus: "Sie haben die Zahl <ihrZahl> eingegeben!"
- Tipp: Mit try-catch können Fehler zur Laufzeit abgefangen werden.
∇ LÖSUNG
Robuste Benutzereingabe:"
# ******** TITEL: Robuste Benutzereingabe ********
# Version: V1.0
# Autor: ARJ
# Datum: 29.1.2020
# Beschreibung: Robuste Benutzereingabe mit try-catch
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration & Initialisierung
[string]$meinString = "" # Benutzereingabe
[int]$meineZahl = 0 # Variable fuer die Benutzereingabe als Zahl
cls # Bildschirm loeschen
$meinString = read-host "Bitte eine Zahl von 0 bis 10 eingeben"
if($meinString -eq "")
{
write-host "Keine Eingabe gemacht!"
}
else
{
try
{
0 + $meinString | Out-Null # Pruefen, ob sich mit $meinString rechnen lässt
# Das Resultat geht in den Müll, weil wir das ja nicht weiter benötigen: | Out-Null
# Da es sich nun definitiv um eine Zahl handelt, bedarf es beim Umkopieren in eine Integer-Variable
# einem Typecast, um das Typenkonzept nicht zu verletzen
# Typecast bedeutet: Mach aus $meinString einen [int] und weise den der Variable $meineZahl zu (siehe folgende Zeile)
$meineZahl = [int]$meinString #Wenn sie hier ankommen, laesst sich mit dieser Zahl rechnen
if([int]$meineZahl -lt 0)
{
write-host "Ihre Zahl ist zu tief!"
}
else
{
if($meineZahl -gt 10)
{
write-host "Ihre Zahl ist zu hoch!"
}
else
{
write-host "Sie haben die Zahl $meineZahl eingegeben!"
}
}
}
catch
{
# Die Pseudorechnung hat nicht funktioniert, weil es sich bei $meinString nicht um eine Zahl handelt
# und wir landen im catch-Bereich. Der Programmabsturz wurde abgefangen eben gecatched.
write-host "Bitte keine Texteingabe!"
}
}
#Programmende
3. Abstraktion / Funktion / Methode / Filter
3.1 Funktionen / Methoden
Wie die meisten Programmiersprachen kann auch PowerShell mehrere Statements zu einer Funktion zusammenfassen. Sie vereinfacht die Wiederverwendung von Code und hilft bei der Strukturierung von Scripts.
Auch wenn Funktionen in PowerShell für die Benutzer von VBScript oder PHP auf den ersten Blick vertraut aussehen, so gibt es doch einige gravierende Unterschiede.
Sowohl Cmdlets als auch in Bibliotheken bereitgestellte Funktionen (Methoden bei Objekten) stellen sogenannte Abstraktionen dar. Oft liefern diese einen Rückgabewert (ReturnValue)
an das aufrufende Programm zurück. Es lassen sich aber auch eigene Funktionen erstellen.
Im Quellcode müssen diese vor ihrem Aufruf definiert werden.
- Funktion - Variante 1: Die param-Angabe bestimmt dabei erforderliche Übergabeparameter und definiert deren Namen. Über diese Namen kann ein Parameter beim Aufruf auch explizit gesetzt werden.
function myFunction
{
param([int]$parameter1, [string]$parameter2, ...) #Parameter definieren
... # Hier steht der Inhalt der Funktion
return ... # Damit kann ein allfälliger Rückgabewert an den Aufrufer zurückgegeben werden
}
Aufruf der Funktion:
c:\user\...> $resultat = myFunction -parameter2 "Text" -parameter1 12 ...
- Funktion - Variante 2: Kurzschreibweise ohne "param" Bei der Übergabe muss die hier definierte Reihenfolge eingehalten werden.
function myFunction ([int]$parameter1, [string]$parameter2, ...)
{
... # Hier steht der Inhalt der Funktion
return ... # Damit kann ein allfälliger Rückgabewert an den Aufrufer zurückgegeben werden
}
Aufruf der Funktion:
c:\user\...> write-host myFunction 12 "Text" ...
- Funktion - Variante 3: Übergabe von Werten via Argumentübergabe mittels der vordefinierten Variable $args (=Array) und $arg.count (=Anzahl übergebener Parameter).
function myFunction
{
... # Hier steht der Inhalt der Funktion
$args[...] # Der erste Wert hat den Index 0
... # Hier steht der Inhalt der Funktion
return ... # Damit kann ein allfälliger Rückgabewert an den Aufrufer zurückgegeben werden
}
Aufruf der Funktion:
c:\user\...> myFunction 12 "c:\" … | out-file resultat.txt
(Hinweis: Beim Aufruf mit mehreren Parametern müssen die Parameter mit Leerschlag und nicht mit Komma getrennt werden!
Kommata definieren ein Array und werden als solche dann in einem Parameter abgelegt.
Um zusammenhängenden Text in einen Parameter zu packen müssen Sie ihn mit " " einfassen.)
Function-Beispiel 1:
function verdoppeln([float] $zahl)
{
[float]$myresult = 0.0
$myresult = 2 * $zahl
return $myresult
}
function verdoppeln_alternativ([float] $zahl)
{
return (2 * $zahl) #Ginge auch ohne ()
}
#MAIN
cls
[float]$wert = 0.0
[float]$resultat = 0.0
[int]$umdrehungen = 0
$wert = read-host "Bitte Reifenradius in Millimeter eingeben"
$resultat = verdoppeln $wert
write-host "Der Reifenradius $wert Millimeter ergibt einen Reifendurchmesser von $resultat Millimeter"
$umdrehungen = read-host "Bitte Anzahl der Radumdrehungen eingeben"
$resultat = [Math]::Pi * [float]$umdrehungen * (verdoppeln $wert) / 1000 #[Math]::Pi → Konstante Pi 3.14...
write-host "Nach $umdrehungen Radumdrehungen wurde eine Wegstrecke von $resultat Meter zurückgelegt"
Function-Beispiel 2:
function myFunction # Funktion
{
[int]$a = 5
[string]$b = "Hello World"
$a
#$b # Hier (ohne # Kommentarzeichen) $b oder return $b haben denselben Effekt.
return $b # Rückgabewert (siehe Kommentar eine Zeile höher)
} # Würde man sowohl $b und auch return $b schreiben, hätte man mit $a drei Werte
$r = myFunction # Funktionsaufruf
write-host $r # Ist eigentlich ein Array und enthält Werte von $a und $b
write-host $r[0] # Der Beweis: $r ist ein Array. In Element 0 steht Wert von $a
write-host $r[1] # In Element 1 steht Wert von $b
#write-host $r[2] # Würde man in der obigen Funktion sowohl $b (aktuell auskommentiert) und auch return $b schreiben,
# hätte man hier mit sogar drei Elemente in $r ($a, $b, $b). Dies macht allerdings keinen Sinn!
Function-Beispiel 3:
function MyPing # Funktion
{
$CompName = $args[0] # Parameterübergabe Erstes Argument
$IP = $args[1] # Parameterübergabe Zweites Argument
Test-Connection $CompName
Test-Connection $IP
}
MyPing myhostname 192.168.1.10 # Funktionsaufruf myhostname = 1. Argument, 192.168.1.10 = 2. Argument
Function-Beispiel 4:
function Start-App([String] $AppName)
{
foreach($App in $input) {
if($App.Name -like $AppName)
{
Start-Process "explorer.exe" -ArgumentList ("shell:AppsFolder\" + $App.AppID)
}
}
}
Get-StartApps | Start-App "Paint*" # Daten über Pipe an Funktion übergeben
Function-Beispiel 5:
function myMessage ($string, $title='PowerShell Message')
{
[windows.forms.messagebox]::Show($string, $title)
}
myMessage "Diesen Text anzeigen!"; #Funktionsaufruf
Function-Beispiel 6:
$nl = [System.Environment]::NewLine # oder `
function myHallo
{
param $name
$date = Get-Date
If ($date.Hour -lt 12 -and $date.Hour -gt 6)
{
"Guten Morgen $name,$nl Es ist $($date.DateTime)"
}
elseif ($date.Hour -gt 12 -and $date.Hour -lt 19)
{
"Guten Tag $name,$nl Es ist $($date.DateTime)"
}
elseif ($date.Hour -gt 19 -and $date.Hour -lt 24)
{
"Guten Abend $name,$nlEs ist $($date.DateTime)"
}
}
myHallo -name Stefan Rehwald # Funktionsaufruf
myHallo -name "Konrad Ernst Otto Zuse" # Funktionsaufruf
myHallo "Rudolf Diesel" # Funktionsaufruf
$User = "Nikolaus Kopernikus"
myHallo $User #Funktionsaufruf
∇ AUFGABEN
- Zahl quadrieren: Es soll eine eigene Funktion erstellt werden. Diese Funktion soll eine Zahl quadrieren und als Funktionsrückgabewert an den Aufrufer,
in diesem Falls das Hauptprogramm, das Resultat zurückgegeben werden.
- SinusBerechnen:
- Erstellen Sie das Programm "SinusBerechnen" anhand des nachfolgenden Struktogramm-Bildes.
- Der Sinuswert eines bestimmten Winkels lässt sich mit einer Reihenentwicklung näherungsweise berechnen. Umso mehr Durchgänge ausgeführt werden, desto präziser wird das Resultat. (Siehe Formel)
- Die Reihenentwicklung verlangt eine Winkeleingabe im Bogenmass (360º entsprechen 2π)
- Die Winkeleingabe soll für den User im Gradmass (0º bis 360º) erfolgen.
- Die Sinusberechnung (Algorithmus) muss als Funktion gestaltet werden.
- Nach der Resultatsausgabe soll eine Abfrage mit Weiter [Y/N] eine weitere Berechnung ermöglichen.
- KleinstesGemeinsamesVielfach:
- Erstellen Sie das Programm "KleinstesGemeinsamesVielfach" anhand des nachfolgenden Struktogramm-Bildes.
- Mit Modulo ist die Restwertfunktion gemeint.
- Die kgV-Berechnung muss als Funktion implementiert werden.
- Nach erfolgter Resultatsausgabe soll der User mit Weiter [Y/N] gefragt werden, ob er eine weitere Berechnung wünscht.
∇ LÖSUNGEN
- Zahl quadrieren:
# Titel: Zahl quadrieren
# Autor: ARJ
# Datum: 3.3.2020
set-psdebug -strict
# Meine Funktion
function quadrieren([int] $wert)
{
$wert = $wert * $wert
return $wert
}
# Variablendeklaration + Initialisierung
[int]$zahl1 = 0
[int]$zahl2 = 0
[int]$resultat1 = 0
[int]$resultat2 = 0
# Hauptprogramm
cls
$zahl1 = read-host "Bitte eine Zahl eingeben"
$zahl2 = read-host "Bitte noch eine Zahl eingeben"
$resultat1 = quadrieren($zahl1) # Funktionsaufruf
$resultat2 = quadrieren($zahl2) # Funktionsaufruf
# Ausgabe
write-host "Die Zahl $zahl1 quadriert ergibt $resultat1"
write-host "Die Zahl $zahl2 quadriert ergibt $resultat2"
- SinusBerechnen:
# ******** TITEL: Sinus berechnen ********
# Version: 1.0
# Autor: ARJ
# Datum: 15. Juli 2021
# Beschreibung: Musterloesung zu Sinus berechnen
# Verwendet wird die Reihenentwicklung fuer die Sinusfunktion.
# Der Benutzer kann angeben, wieviele Terme berechnet werden.
# Diese Reihenentwicklung verlangt einen Winkel im Bogenmass.
# Der Benutzer darf allerdings den Winkel im Gradmass eingeben.
# Darum muss zuerst eine Umrechnung vom Gradmass in das Bogenmass durchgefuehrt werden.
# Entwicklung des Algorithmus: ARJ
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration und Initialisierung
[double]$GradMass = 0.0 # Eingabe eines Winkelwertes im Gradmass
[double]$BogenMass = 0.0 # Eingabe eines Winkelwertes im Gradmass
[int]$Genauigkeit = 0 # Anzahl Berechnungsdurchgaenge und somit Genauigkeit des Resultats
[char]$weitereBerechnungen = 'n'
[double]$dieAbweichung = 0.0 # Abweichung zum tatsaechlichen Sinuswert
[double]$ResultatSinus = 0.0 #Resultat
cls #Bildschirm loeschen
# Es folgt die Function
function SinusBerechnen([double]$meinWinkelBogen, [int]$anzDurchgaenge)
{ [int]$derzaehler = 0 # Zaehler fuer Anzahl Durchgaenge, lokale Variable
[double]$diePotenz = 0.0 # Potenzberechnung bzw. Resultat, lokale Variable
[double]$dieFakultaet = 0.0 # Fakultaetsberechnung bzw. Resultat, lokale Variable
[double]$derQuotient = 0.0 # Quotientenberechnung bzw. Resultat, lokale Variable
[double]$meinSinuswert = 0.0 #Resultat, lokale Variable
# Alle Startwerte fuer die Berechnung setzen (siehe Struktogramm)
$derzaehler = 2.0
$meinSinuswert = $meinWinkelBogen
$diePotenz = $meinWinkelBogen * $meinWinkelBogen * $meinWinkelBogen
$dieFakultaet = 2.0
$derQuotient = 1.0 * 2.0 * 3.0
# Berechnung in einer Schleife gemaess Anzahl Durchgaenge, was die Genauigkeit ausmacht
while ( $derZaehler -le $anzDurchgaenge )
{
if ( ($derzaehler % 2) -eq 0 )
{
$meinSinuswert = $meinSinuswert - $diePotenz / $derQuotient
}
else
{
$meinSinuswert = $meinSinuswert + $diePotenz / $derQuotient
}
# Werte fuer den neuen naechsten Durchgang berechnen
$dieFakultaet = $dieFakultaet + 2
$derQuotient = $derQuotient * $dieFakultaet * ($dieFakultaet + 1.0)
$diePotenz = $diePotenz * $meinWinkelBogen * $meinWinkelBogen
$derzaehler = $derzaehler + 1
}
return $meinSinuswert
}
Write-Host "BERECHNUNG DES SINUS MIT EINER REIHENENTWICKLUNG v1.0"
do
{
Write-Host "*****************************************************"
# Winkelwert eingeben
$GradMass = Read-Host "Winkel im Gradmass eingeben"
# Winkel ins Bogenmass umrechnen
$BogenMass = $meinWinkelGrad / 180 * [math]::pi #[math]::pi ist die Pi-Konstante auf der Math-Library
# Anzahl Berechnungsdurchgaenge eingeben (Hat Einfluss auf die Genauigkeit des Resultats)
$Genauigkeit = Read-Host "Genauigkeit [ 1(ungenau) bis 100(sehr genau) ]"
#Nun folgt der Funktionsaufruf
$ResultatSinus=SinusBerechnen $BogenMass $Genauigkeit #Funktionsaufruf
# Ausgabe
Write-Host "Fuer den" $meinWinkelGrad "Grad Winkel wurde ein Sinuswert von" $ResultatSinus "berechnet"
$dieAbweichung = $ResultatSinus - [math]::sin($BogenMass) #Korrekter Sinus-Wert berechnen
$dieAbweichung = [math]::Round($dieAbweichung,6) #Resultat auf 6 Nachkommastellen runden
Write-Host "Die Abweichung zum korrekten Sinuswert betraegt auf 6 Nachkommastellen gerundet" $dieAbweichung
$weitereBerechnungen = Read-Host "Weitere Berechnungen? [y/n]"
} while ( $weitereBerechnungen -eq 'y' )
Write-Host "*****************************************************"
Write-Host "Das Programm wurde beendet!"
# ******** Hier endet das Programm ********
- KleinstesGemeinsamesVielfach: Es existiert noch keine Musterlösung.
3.2 Filter
Filter werden identisch zu den Funktionen definiert.
Ein Filter ist eine Art Funktion die auf jedes Objekt angewendet wird, das durch eine Pipeline angeliefert wird.
Eine Funktion wird einmal für alle Objekte in der Pipeline gemeinsam aufgerufen,
ein Filter jeweils einmal für jedes Objekt. Um also beispielsweise einen Befehl zu entwickeln,
der die Summe einer bestimmten Eigenschaft berechnet, wird eine Funktion eingesetzt, was jedoch auch den Nachteil hat,
dass die Pipeline-Verarbeitung solange angehalten wird, bis sämtliche Objekte zur Verfügung stehen.
Bei einem Filter kann die Pipeline weiterarbeiten, obwohl das vorhergehende Cmdlet noch weitere Objekte liefert.
- Filter - Variante 1
filter name { param($parameter1, $parameter2, ...) ... }
- Filter - Variante 2
filter name ($parameter1, $parameter2, ...) {...}
- Filter - Variante 3
filter name { ... $args[...] ... }
Filter-Beispiel 1:
Filter meinFilter
{
Write-Host "Ich bin ein Filter!"
}
PS C:\users\FelixMuster> myFilter
Filter-Beispiel 2:
PS C:\users\FelixMuster> Filter PlusEins { $_ + 1 }
PS C:\users\FelixMuster> Echo 1 | PlusEins
2
Filter-Beispiel 3:
filter meinFilter
{
$_
}
PS C:\users\FelixMuster> @(1,2,3) | meinFilter
# Dasselbe könnte man auch mit dieser Funktion erreichen (Anstatt $_ hier $Input):
function meineFunktion
{
$Input
}
PS C:\users\FelixMuster> @(1,2,3) | meineFunktion
Filter-Beispiel 4:
Das folgende Beispiel zeigt den Unterschied zwischen einer Funktionen und einem Filter,
wenn z.B. auf mehrere Elemente eines Arrays zugegriffen werden soll. Der wesentliche Unterschied zwischen einer Funktion und einem Filter
besteht darin, wie die Pipeline verarbeitet wird. Die Funktion kann mit der Verarbeitung erst beginnen,
wenn das komplette Array in der Variable $Input gespeichert ist. Und dafür braucht es bei grossen Arrys eine Menge Speicher.
Im Gegensatz dazu verarbeitet der Filter jedes Element
sofort nach dem Eintreffen aus der Pipe. Die Variable $_ speichert nur immer ein Element. Bei grossen Arrays arbeitet der Filter daher wesentlich effizienter.
Function meineFunktion
{
ForEach ($i in $Input)
{
If($i -eq 2)
{
$i
}
}
}
PS C:\users\FelixMuster> @(1,2,3) | meineFunktion
# ---------------------------------
#(In Gegensatz zur Funktion benötigt der Filter keinen Loop)
Filter meinFilter
{
If ($_ -eq 1)
{
$_
}
}
PS C:\users\FelixMuster> @(1,2,3) | meinFilter
Filter-Beispiel 5:
Ein Performance-Vergleich Filter zu Funktion:
Function meineFunktion
{
ForEach ($File in $Input)
{
$File
}
}
PS C:\users\FelixMuster> Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue | meineFunktion
# ---------------------------------
Filter meinFilter
{
$_
}
PS C:\users\FelixMuster> Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue | meinFilter
# ---------------------------------
# Definitiv im Vorteil ist der Filter hier:
Filter meinFilter
{
if($_.Name -eq "notepad.exe")
{
"Datei gefunden: $_"
Break
}
}
PS C:\users\FelixMuster> Get-ChildItem C:\Windows -Recurse -ErrorAction SilentlyContinue | meinFilter
3.3 Funktionen mit Filter-Verhalten
Wie die vorangegangenen Beispielen zeigten, wird bei einer Funktion $Input und bei einem Filter $_ verwendet
und es bestehen wesentliche Unterschiede in Verarbeitung und Spicherbedarf.
Nun gibt es aber die Möglichkeit, einer Funktion mit dem Keyword Process dasselbe Verhalten bzw. Performance wie bei einem Filter zu verleihen:
Function meineFunktion
{
Process { $_ }
}
PS C:\users\FelixMuster> Get-ChildItem C:\ -Recurse -ErrorAction SilentlyContinue | meineFunktion
# Bemerkung: Die Funktionsvariante ohne Process hat für spezielle Aufgaben immer noch ihre Berechtigung.
4. Der Powershell-Debugger
Unter Debuggen versteht man das informelle Testen eines Quellcodes durch den SW-Entwickler. Die Powershell-ISE besitzt einen internen Debugger. Bevor der Debugger wirksam werden kann, muss das Programm abgespeichert werden.
Der Debugger stellt folgendes zur Verfügung:
- Programmausführung in Einzelschritten, vorwärts oder rückwärts
- Programmausführung bis zu einem zuvor gesetzten Haltepunkt
- Lesen von Variableninhalten
∇ AUFGABEN
- Erste Erfahrung mit dem Debugger: Bei der Ausführung des folgenden Programms erwarten sie 2 Mal eine Reihe mit "*****". Leider tut es dies nicht. Suchen sie mit dem Debugger den Fehler:
(Tipp: Haltepunkt richtig setzen und Variableninhalt überprüfen.)
# ******** TITEL: Debugger-Uebung 1 ********
# Version: V1.0
# Autor: ARJ
# Datum: 29.1.2020
# Beschreibung: Den Debugger einsetzen
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration & Initialisierung
[int]$i = 0
cls # Bildschirm loeschen
while($i -le 10)
{
if( ($i % 5) -eq 0) # Nach fuenf "*" ...
{
write-host "" # ... auf eine neue Zeile
}
write-host -NoNewline "*" # write-host ohne Zeilenvorschub
$i++
}
write-host ""
#Programmende
- Debuggen des Skripts "Potenzieren": Das folgende Programm ist eine Powershell-Implementierung (Algorithmus) für die Berechnung die Potenz-Berechnung und weist zwei Fehler auf.
Spüren sie diese mit dem Debugger auf und beheben sie die Fehler!
Berechnugsbeispiele:
- 32 = 3*3 = 9
- x0 = 1 (x=beliebige Zahl)
- 2-3 = 1 / (2*2*2) = 1/8 = 0.125
Kopieren sie den folgenden Script in ihren Powershell-Scripteditor und suchen sie mit dem Debugger die zwei Fehler! Der Debugger ermöglicht ihnen:
- Das Programm in Einzelschritten zu durchlaufen.
- Das Programm bis zu einem Breakpoint vorwärts laufen zu lassen.
Tipp: Breakpoint an sinnvoller Stelle innerhalb der Iterationen setzen um Schleifendurchgänge zu prüfen.
- Sich Variableninhalte anzeigen zu lassen.
Korrigieren sie anschliessend die Fehler und prüfen sie das Programm erneut.
# ******** TITEL: Potenzieren (Script ist absichtlich Fehlerhaft!) ********
# Version: 1.0
# Autor: ARJ
# Datum: 15. Januar 2020
# Beschreibung: Potenzieren mit Iteration
# Fehlerhaftes Programm fuer Debugg-Uebung
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration inklusive Initialisierung auf Null
[int64]$dieBasis = 0 # Basis der Potenz
[int64]$derExponent = 0 # Exponent der Potenz
[int64]$dasResultat = 0 # Resultat der Berechnung
[int64]$i = 0 # Laufvariable
cls #Bildschirm loeschen
# *** EINGABE ***
$dieBasis = Read-host "Basis"
$derExponent = Read-host "Exponent"
$dasResultat = 1
$i = 0
if( $derExponent -lt 0) # Exponent negativ
{
while($i -ge $derExponent)
{
$dasResultat = $dasResultat * $dieBasis
$i = $i - 1
}
# *** AUSGABE ***
write-host "$dieBasis hoch $derExponent ergibt" (1/$dasResultat)
}
else # Exponent positiv
{
while($i -lt $derExponent)
{
$dasResultat = $dasResultat
$i = $i + 1
}
# *** AUSGABE ***
write-host "$dieBasis hoch $derExponent ergibt" ($dasResultat)
}
# ******** Hier endet das Programm ********
- Debuggen des Skripts "Pi-Berechnung": Das folgende Programm ist eine Powershell-Implementierung (Algorithmus) für die Berechnung der Kreiskonstanten Pi und weist zwei Fehler auf.
Spüren sie diese mit dem Debugger auf und beheben sie die Fehler!
(Diese Formel stammt vom indischen Mathematiker und Astronomen Kelallur Nilakantha Somayaji 1444-1544)
Kopieren sie den folgenden Script in ihren Powershell-Scripteditor und suchen sie mit dem Debugger die zwei Fehler. Korrigieren sie anschliessend die Fehler und prüfen sie das Programm erneut.
# ******** TITEL: Pi berechnen nach Somayaji (Script ist absichtlich fehlerhaft!) ********
# Version: 1.0
# Autor: ARJ
# Datum: 15. Januar 2020
# Beschreibung: Pi berechnen nach Somayaji
# Fehlerhaftes Programm fuer Debugg-Uebung
# Programmiermodus einschraenken
set-psdebug -strict
set-strictmode -version latest
# Variablen-Deklaration inklusive Initialisierung
[int]$anzTerme = 0 # Anzahl Terme oder Iterationen
[int]$aktTerm = 1 # Aktueller Term oder Durchgang
[bool]$numOK = $false # Eingabe ist Zahl
[double]$thePI = 3.0 # Wert von PI, Initialisiert mit Startwert (Bem.: Pi = 3.141562...)
[double]$theDivisor = 0.0 # Temporaerer Divisor
[int]$i # Hilfsvariable, Laufvariable
# *** EINGABE ***
do
{
try
{
cls #Bildschirm loeschen
$numOk = $true
$anzTerme = Read-host "Anzahl Iterationen 1..100"
} # end try-Part
catch
{
$numOK = $false
} # end catch-Part
} until (($anzTerme -ge 1 -and $anzTerme -le 100) -and $numOK)
# *** VERARBEITUNG ***
while($aktTerm -le $anzTerme)
{
# Divisor berechnen z.B. "3 hoch 3 minus 3"
$i = 0 # $i zuruecksetzten
$theDivisor = 0 # $theDivisor zuruecksetzten
while($i -le 3)
{
$theDivisor = $theDivisor * (2 * $aktTerm + 1)
$i++
}
$theDivisor = $theDivisor - (2 * $aktTerm + 1)
# Ganzer Term berechnen und mit vorangegangenem Resultat verrechnen
if($aktTerm % 2) # Aktueller Term ungerade
{
$thePI = $thePI + (4 / $theDivisor)
}
else # Aktueller Term gerade
{
$thePI = $thePI - (4 / $theDivisor)
}
$aktTerm++
}
# *** AUSGABE ***
write-host "Nach $AnzTerme Durchgaengen ergibt die Berechnung fuer Pi $thePi"
write-host "Die Pi-Konstante in Powershell lautet" ([math]::Pi)
write-host "Das ergibt eine Abweichung von " ($thePi - [math]::Pi)
# ******** Hier endet das Programm ********
- Weitere Programme debuggen:
- Wählen sie einen ihrer bisher erstellten Powershell-Scripte aus. Dieser sollte verschachtelte Iteartionen beinhalten.
- Starten sie den Debugger
- Setzen sie geeignete Breakpoints innerhalb einer Iteration
- Lesen sie die Variablenwerte beim Breakpoint aus
- Lassen sie das Programm in Einzelschritten ablaufen (Step-by-Step)
∇ LÖSUNGEN
Zu diesen Aufgaben existieren keine Musterlösungen.
5. Objektorientierte Programmierung
5.1 Das objektorientierte Programmierparadigma
Die objektorientierte Programmierung (OOP) ist ein auf dem Konzept der Objektorientierung basierendes Programmierparadigma. Die Grundidee besteht darin, die Architektur einer Software an den Grundstrukturen desjenigen Bereichs der Wirklichkeit auszurichten, der die gegebene Anwendung betrifft.
- Alles ist ein Objekt
- Objekte kommunizieren durch das Senden und Empfangen von Nachrichten
- Objekte haben ihren eigenen Speicher
- Jedes Objekt ist die Instanz einer Klasse
- Die Klasse beinhaltet das Verhalten aller ihrer Instanzen
Der hauptsächliche Unterschied zwischen prozeduraler und objektorientierter Programmierung ist die Beziehung zwischen Daten und Funktionen. Während bei der objektorientierten Programmierung Daten und Funktionen, die auf diese Daten angewandt werden können, in Objekten zusammengefasst werden, haben bei der prozeduralen Programmierung Daten und Funktionen keinen Zusammenhalt.
Beispiel:
Aufgrund eines Bauplans (Class) werden Exemplare (Objects) davon erzeugt (instanziert). Nämlich in diesem Fall ein Objekt mit dem Namen «Lightning», ein anderes mit dem Namen «Blizzard» und ein drittes mit dem Namen «Thunder». Diese Objekte können nun über ihre Objektnamen angesprochen werden.
Die Objekte besitzen:
- Eigenschaften oder Attribute (Properties), wie z.B. Farbe, Grösse und weitere Merklmale
- Methoden, Funktionen (Methods). Methoden können Parameter erhalten, die beim Aufruf übergeben werden müssen, und einen Rückgabewert besitzen, den sie am Ende dem Aufrufer zurückgeben. Beispielsweise hat die Methode «addiere» die Parameter Zahl 1 und Zahl 2 und gibt als Rückgabewert die Summe der Zahlen zurück.
Verändern von Eigenschaften (Zugriff auf Properties):
Ausführen von Funktionen (Methods):
Spezielle Methoden zur Erzeugung und Zerstörung von Objekten heissen Konstruktoren Create() beziehungsweise Destruktoren Kill().
5.2 Zugriff auf Eigenschaften und Methoden von Objekten
Erklärt an einem Beispiel das voraussetzt, dass der Prozess Notepad auf dem System gestartet ist:
# Alle Eigenschaften und Methoden des Prozess-Objekts «Notepad» anzeigen:
Get-Process Notepad | Get-Member # Get-Process und Get-Member sind CmdLet's
# 1. Variante → Methode ausführen:
# (Mit dem CmdLet «Get-Process» das Prozess-Objekt «Notepad» ansprechen und die Methode «GetType()» aufrufen)
(get-process notepad).GetType()
# 2. Variante → Methode ausführen:
# (Mit dem CmdLet «Get-Process» das Prozess-Objekt «Notepad» in der Object-Variablen «myObj» speichern
# und anschliessend über die Object-Variable die Methode «GetType()» ausführen)
[Object] $myObj # Object-Variable deklarieren
$myObj = get-process Notepad # Object-Variable zuweisen
$myObj.GetType() # Über Object-Variable eine seiner Methoden ausführen
# Eigenschaft Prozess-ID anzeigen:
(Get-Process Notepad).Id
$myObj.Id
Wenn man mit einer PowerShell-Pipeline arbeitet und sich auf das Objekt beziehen möchten, das sich gerade in der Pipeline befindet, kann man eine der beiden automatisch generierten und völlig gleichwertigen Variablen $PSItem oder $_ nutzen. Dazu ein Beispiel:
Get-Process | Foreach-Object { write-host $PSItem.ProcessName $PSItem.CPU}
Get-Process | Foreach-Object { write-host $_.ProcessName $_.CPU}
Get-Process | Foreach-Object { write-host $_.ProcessName }
Get-Process | Foreach-Object { $_.ProcessName }
5.3 Statische Eigenschaften und Methoden von .Net-Klassen verwenden
[<Klassenname>]::Eigenschaft
[<Klassenname>]::Methode(<Parameter>)
Beispiele mit der .Net-Klasse «Math»
[Math]::Pi #Konstatnte Pi
$x = 0.5; [Math]::Sin($x) #Sinuswert ermitteln
$y = 2; [Math]::Sqrt($y) #Quadratwurzel ermitteln
Beispiele mit der .Net-Klasse «Convert»
$Bin2Dez = [Convert]::ToInt32("101101010101",2) #Resultat: 290110
$Oct2Dez = [Convert]::ToInt32("1234567",8) #Resultat: 34239110
$Dez2Hex = [Convert]::ToString("65098",16) #Resultat: "FE4A"
$Dez2Bin = [Convert]::ToString("12345",2) #Resultat: "11000000111001"
5.4 Klassen instanzieren
Bei Powershell ist alles ein Objekt mit Eigenschaften und Methoden, sogar der Integer und der String.
Beispiel:
[string] $myStr = "Hello"
$myStr | Get-Member
$myStr.Length # Ergibt den Wert 5
$myStr.ToUpper() # Ergibt HELLO
Beim folgenden Beispiel wird nicht ein neues Objekt erzeugt, sondern eine Objektvariable erstellt
und dieser die Referenz auf das Prozessobjekt "notepad" zugewiesen.
Das Prozessobjekt "notepad" wurde von Betriebssystem zu dem Zeitpunkt erstellt,
als das Programm notepad.exe gestartet wurde:
[object]$myObject
$myObject = Get-Process notepad
Es soll mit New-Object ein Objekt von einer .Net-Klasse erstellt werden.
Dies soll anhand eines Beispiels mit der Klasse Net.WebClient gezeigt werden.
Diese enthält die Methode DownloadFile, mit der eine Datei aus dem Internet geladen und
lokal gespeichert werden kann:
$web = New-Object -typename Net.WebClient
$web.DownloadFile("http://edu.juergarnold.ch/fach_it/wpssummary/titel.jpg", "C:\Users\FelixMuster\Pictures\titel.jpg")
Falls an ihrem Standort ein Proxiserver mit z.B. IP=192.0.200.200 an Port=8080 existiert, muss folgendes ergänzt werden:
$web = New-Object -typename Net.WebClient
$proxy = New-Object System.Net.WebProxy("http://192.0.200.200:8080")
$web.proxy = $proxy #WebClient über diesen Proxy leiten
$proxy.UseDefaultCredentials = $true #Benutzt Benutzername & PW des aktuellen Standorts
$web.DownloadFile("http://edu.juergarnold.ch/fach_it/wpssummary/titel.jpg", "C:\Users\FelixMuster\Pictures\titel.jpg")
∇ AUFGABEN
- Zeigen sie alle Eigenschaften und Methoden des CmdLets Get-Process an.
- Starten sie die Applikation notepad.exe und zeigen sie sich danach alle
Methoden, Eigenschaften und Aliase) von notepad.exe an.
- Zeigen sie sich nun ausschliesslich die Methoden von notepad.exe an.
- Zeigen sie sich nun ausschliesslich die Eigenschaften/Properties von notepad.exe an.
- Lassen sie sich die Startzeit von "notepad.exe" anzeigen.
- Versuchen sie die Startzeit von notepad.exe mit der aktuellen Uhrzeit zu überschreiben. Was stellen sie fest?
- Versuchen sie den notepad.exe Prozess zu beenden. (kill)
- Lassen sie sich alle Prozesse anzeigen, die "win" im Namen haben.
∇ LÖSUNGEN
#Aufgabe 1:
get-process | get-member
#Aufgabe 2:
start-process notepad.exe # Programm "notepad.exe" starten
get-process notepad | get-member # Methoden, Eigenschaften und Aliase von "notepad.exe" anzeigen
# Wenn der Prozess notepad.exe nicht gestartet wurde, erscheint eine Fehlermeldung
#Aufgabe 3:
get-process note* | get-member -membertype Properties # Nur die Eigenschaften anzeigen (Tipp: * → Wildcard!)
# "-membertype AliasProperty" würden nur die Aliase angezeigt
#Aufgabe 4:
get-process note* | get-member -membertype Method # Nur die Methoden anzeigen
#Aufgabe 5:
# Nun werden wir uns Eigenschaften des Prozessobjekts anzeigen lassen
(get-process notepad).starttime # Startzeit von "notepad.exe" anzeigen
# starttime ist eine Eigenschaft des Prozess-Objekts "notepad"
#Aufgabe 6:
(get-process note*).StartTime = get-date # get-date liefert ihnen übrigens das aktuelle Datum
# StartTime ist ReadOnly und kann/darf somit nicht überschrieben werden
#Aufgabe 7:
get-process notepad.getType # Funktioniert nicht, weil die Klammern um "get-process notepad" fehlen
(get-process notepad).getType # Funktioniert nicht, weil die Klammern nach "getType" fehlen
(get-process notepad).getType() # Schon besser! (get-process notepad) präsentiert das Prozessobjekt
(get-process notepad).tostring() # Zeigt Standard-Infotext
(get-process notepad).kill() # Das ist der korrekte Befehl, um den Prozess "notepad.exe" zu beenden
#Aufgabe 8:
Get-Process | Foreach-Object { # Alle Prozesse anzeigen, die "win" im Namen haben
if ($_.ProcessName –like "*win*") # Achtung, funktioniert so nicht: $_.ProcessName –eq "*win*"
{
$_.ProcessName
}
}
6. Standardobjekte in Powershell
6.1 Zählfunktionen
Um Dinge zu zählen gibt es z.B. das Cmdlet Measure-Object.
Get-Process * | Measure-Object
Get-ChildItem -Filter *.txt | Measure-Object -Property length -Maximum -Minimum -Average –Sum
Get-Content MeinText.txt | Measure-Object -line -char –word -IgnoreWhiteSPace
(Get-ChildItem C:\Users\MeinNamen -Recurse –force).count
6.2 String-Methoden und Funktionen
Bei Zeichenketten (Strings) handelt es sich immer um Objekte.
"Hallo Welt" | Get-Member # Zeigt alle Methoden und Eigenschaften eines Stringobjektes an
$h = "Hallo" # Zeichenkette zusammenfügen
$w = "Welt"
$h + " " + $w # Eine Alternative wäre: "$h $w"
$st = @("Hallo", "Welt") # Elemente eines String-Arrays zusammenfügen
"$st" # Dazwischen wird automatisch ein Leerzeichen eingefügt
[string]$s1 = "Hallo"
[string]$s2 = "Welt"
-join($s1,$s2) # Verknüpfung mit join-Operator. Kein Leerzeichen dazwischen
$s1,$s2 -join " " # Dazwischen wird nun ein Leerzeichen eingefügt
("Hallo Welt").split(" ") # Zeichenkette am Leerzeichen zerlegen. Das Leerzeichen entfällt
("Hallo Welt").Substring(2,5) # Teil der Zeichenkette herauslösen
("Hallo Welt").Remove(2,3) # Teil der Zeichenkette löschen
("Hallo Welt").Replace("Hallo","Neue") # Teil der Zeichenkette ersetzen
("Hallo Welt").Contains("ll") # Teil der Zeichenkette suchen. Liefert $True oder $False zurück
("Hallo Welt").IndexOf("ll") # Position eines Zeichens oder Substrings ermitteln
[string]$s1 = "Eisen"
[string]$s2 = "Bahn"
("EisenBahn").CompareTo($s1 + $s2) # Liefert den Wert 0 → Reihenfolge identisch
("BahnEisen").CompareTo($s1 + $s2) # Liefert den Wert -1 → Reihenfolge umgekehrt
("Eisen Bahn").CompareTo($s1 + " " + $s2) # Liefert den Wert 0 → Reihenfolge identisch
("EisenBahn").CompareTo($s1 + " " + $s2) # Liefert den Wert 1
("Eisen Bahn").Equals($s1 + " " + $s2) # Liefert den Wert $True → Zeichenketten sind identisch
"eisenbahn".ToUpper() # Wandelt die komplette Textzeile in Grossbuchstaben um
$nl = [System.Environment]::NewLine # Erzeugt eine Neue Zeile bei der Ausgabe
write-host "Eisen $nl Bahn"
∇ AUFGABEN
- Lesen sie eine Textzeile ein und geben sie anschliessend die letzten drei Buchstaben des eingegebenen Textes wieder aus.
- Machen sie aus dem String: [string]$myStr = "meinText.txt" den folgenden String: meinText.bck.
- Gegeben sei:
[string]$myStr = ""
$myStr = read-host "Text eingeben, in der Art textteil1.textteil2"
Ersetzen sie nun den Teil nach dem Punkt durch "xxx". (Bsp.: meinText.bck wird zu meinText.xxx)
Beachten sie, dass nach dem Punkt nicht unbedingt drei Zeichen folgen müssen wie bei der vorangegangenen Aufgabe.
(Bsp.: meinText.abcdefgh soll auch zu meinText.xxx werden)
∇ LÖSUNGEN
# Aufgabe-1
[string]$mytxt = "meinText.txt"
Write-Host $mytxt.Substring($mytxt.Length - 3)
# Aufgabe-2
[string]$mytxt = "meinText.txt"
Write-Host $mytxt.Replace("txt", "bck")
# Aufgabe-3
[string]$myTxt = "Hallo.Welt"
[string[]]$myTxtArr = $myTxt.split(".")
$myTxt = $myTxtArr[0] + ".xxx"
6.3 DateTime-Methoden und Funktionen
In einer DateTime-Variable lassen sich Zeit- und/oder Datumswerte abspeichern und verarbeiten.
Da ein Datum immer ortsabhängig (Culture) angegeben werden muss, müssen wir bei der Eingabe
immer über das ortssensitive Cmdlet Get-Date operieren.
Get-Date | get-member -MemberType Property # Eigenschaften anzeigen
Get-Date | get-member -MemberType Method # Methoden anzeigen
$info = new-object system.globalization.datetimeformatinfo
$info.DayNames
$info.MonthNames
[DateTime]$Zeitpunkt = Get-Date "12.3.2015 15:33" # Konkretes Datum/Uhrzeit als String eingeben
Write-Host $Zeitpunkt
[DateTime]$Zeit = Get-Date "15:33" # Konkrete Zeit als String eingeben
Write-Host $Zeit
[DateTime]$Datum = Get-Date "12.3.2015" # Konkretes Datum eingeben (Uhrzeit ist dann 00:00:00)
Write-Host $Datum
"Day: " + $Datum.Day
"Month: " + $Datum.Month
"Year: " + $Datum.Year
"Hour: " + $Zeit.Hour
"Second: " + $Zeit.Second
$Zeitpunkt.DayOfYear # Der wievielte Tag im Jahr?
$Zeitpunkt.TimeOfDay # Tabelle mit allen Werten
$Zeitpunkt.AddDay(2) # Anzahl Tage dazuzählen
$Zeitpunkt.AddDay(-2) # Anzahl Tage abzählen
([datetime] "1.2.2006" - [datetime]"1.1.2006").TotalDays # Datumsdifferenz
function tillXmas () # Eigene Funktion erstellen
{
$now = [DateTime]::Now
[Datetime]("25.12." + [string]$now.Year) - $Now
}
∇ AUFGABE
Diese Aufgabe handelt vom Standardobjekt DateTime bzw. DateTime-Methoden und DateTime-Funktionen:
Berechnen sie, wievielmal mal sie noch schlafen müssen, bis das nächste Mal der Osterhase kommt.
∇ LÖSUNG
[DateTime]$Datum1 = Get-Date "13.3.2020"
[DateTime]$Datum2 = Get-Date "24.3.2020" #...oder wann auch immer der Osterhase kommt
[int]$anzTage = ($Datum2 - $Datum1).TotalDays
7. Anwendungen mit Powershell
7.1 Ausgabe in eine HTML-Datei
# Hinweis Zeilenumbruch: Für eine bessere Lesbarkeit, wurde die lange Code-Zeilen "ConvertTo-Html" auf mehrere Zeilen umgebrochen.
# Damit dies zu keinem Syntaxfehler führt, muss jede Zeile mit "`" ergänzt werden.
# Hinweis foreach : foreach verlangt das Begin "{" auf der selben Zeile.
get-service | ConvertTo-Html `
-title "MELDUNG SYSADMIN" `
-body (get-date) `
-pre "<p>Erstellt von Felix Muster</p>" `
-post "<p>Weitere Infos siehe <a href=http://felixmuster> http://felixmuster</a></p>" `
-Property Name, Status | foreach {
if($_ -like "*<td>Running</td>*")
{
$_ -replace "<tr>", "<tr bgcolor=green>"
}
elseif($_ -like "*<td>Stopped</td>*")
{
$_ -replace "<tr>", "<tr bgcolor=red>"
}
else
{
$_
}
} > .\ServiceStatus.html
7.2 Mit Dateien arbeiten
#Verzeichnisse prüfen, auslesen oder erstellen
Set-Location Pfadname # Pfad setzen
Get-Location # Pfad auslesen
Test-Path -path Pfadname # Existenz eines Pfades überprüfen ($True oder $False)
Get-Item Verzeichnisname # Verzeichnis lesen
Get-Item Dateiname # Datei lesen
Get-ChildItem Verzeichnisname -include Pattern # Filterfunktion-Einschliesslich; Pattern → *.jpg
Get-ChildItem Verzeichnisname -exclude Pattern # Filterfunktion-Ausschliesslich; Pattern → *.gif
Get-ChildItem Verzeichnisname -recurse # Rekursion → Zeigt auch alle Unterverzeichnisse an
(Get-Item .\meinUnterverzeichnis).delete() # Leeres Unterverzeichnis löschen
$myFile = Get-Item *.txt # Ergibt Array von gefundenen Dateiobjekten
$myFile | Get-Member # Dateiobjekt-Array wird an Get-Member weitergereicht
$myFile = Get-Item .\meinText.txt # Auf Dateieigenschaften von "meinText.txt" zugreifen
Write-Host $myFile.Name # Dateiname anzeigen
Write-Host $myFile.Length # Dateigrösse in Byte anzeigen
$myFileList = get-ChildItem Pfadname -recurse # Zeigt alle Verzeichnisse ab Pfadname an
foreach($myFile in $myFileList) {
if($myFile.PSISContainer -eq $True)
{
Write-Host $myFile.Name
}
}
dir Pfadname -r | foreach { # Zeigt ebenfalls alle Verzeichnisse ab Pfadname an
if ($_.mode –match "d")
{
Write $_.Name
}
}
if ((get-ChildItem Pfadname).exists -eq $True) # Existenzprüfung:
{ # Falls WPS die Datei nicht findet,
write-host OK # auf die eine Operation ausgeführt werden soll,
} # kommt es zu einer Fehlermeldung.
else # Darum muss zuerst deren Existenz überprüft werden.
{
write-host NOK
}
New-Item -Path . -name $xx -ItemType directory # Neues Verzeichnis erstellen
#Dateien erzeugen, kopieren, verschieben, löschen oder umbenennen
# Wichtig für alle folgenden Beispiele:
# Falls der Pfadname relativ angegeben wird, empfiehlt es sich, an erster Stelle den aktuellen Pfad wie folgt zu setzen:
[environment]::CurrentDirectory = Get-Location # Aktueller Pfad setzen, falls relative Pfadangabe folgt
# Eine neue Datei erzeugen
New-Item -ItemType "file" -Path "c:\mypath"
# Eine Datei kopieren
$myFile = Get-Item Dateiname # Z.B. Get-Item .\meinText.txt
$myFile.CopyTo("Pfadname\Dateiname", -1) # -1 → Überschreiben
# Mehrere Dateien kopieren - 1. Variante
$myFileList = get-ChildItem Pfadname\Dateiname
$destDir = "MeinZielPfadname"
foreach($myFile in $myFileList) { # Out-Null soll die Ausgabe unterdrücken
$myFile.CopyTo($destDir +($myFile.Name), -1) | Out-Null
}
# Mehrere Dateien kopieren - 2. Variante
$myFileList = get-ChildItem Pfadname\Dateiname
$destDir = "MeinZielPfadname"
Copy-Item $myFileList $destinationDir -force #force erzwingt Überschreiben
# Eine Datei verschieben
$myFile = Get-Item Dateiname
$myFile.MoveTo("Pfadname\Dateiname")
# Mehrere Dateien verschieben
$myFileList = get-ChildItem Pfadname\*.* # z.B.: $myFileList = get-ChildItem C:\Dokumente\*.txt
$destDir = "MeinZielPfadname"
Move-Item $myFileList $destDir
# Eine Datei löschen
$myFile = Get-Item Dateiname
$myFile.Delete()
# Mehrere Dateien löschen
$myFileList = get-ChildItem Pfadname\*.* # z.B.: $myFileList = get-ChildItem C:\Dokumente\*.txt
Remove-Item $myFileList
# Dateien umbenennen (Eine eigentliche Methode gibt es nicht. Allenfalls kann mit MoveTo()
eine Umbenennung konstruiert werden.
Ein CmdLet existiert allerdings und nennt sich Rename-Item)
$myNewName = Read-Host "Neuer Dateinamen"
$myZaehler = 0
$myFiles = Get-ChildItem *.txt
if($myFiles -ne $NULL)
{
foreach($File in $myFiles) {
$myFullNewName = $myNewName + $myZaehler + ($File.extension)
Rename-Item $File $myFullNewName
$myZaehler++
}
write-host ($myZaehler)" Dateien umbenannt"
}
Mit Dateiattributen arbeiten:
# Es existieren unter MS-Windows folgende Dateiattribute:
# +R bzw -R → Schreibgeschützt setzen (+) bzw. löschen (-)
# +A bzw -A → Archiv setzen (+) bzw. löschen (-)
# +S bzw -S → Systemdatei setzen (+) bzw. löschen (-)
# +H bzw -H → Versteckte Datei setzen (+) bzw. löschen (-)
# (Im folgenden beschäftigen wir uns nur noch mit dem Archive-Attribut)
# In den Dateieigenschaften findet man unter Allgemein/Erweitert das Archive-Attribut:
# Erweiterte Attribute: ⊗ Datei kann archiviert werden
# Das Archiv-Bit kann z.B. von einer Backup-SW ausgewertet werden.
# Archiv-Bit = "1": Die Datei wurde verändert und soll bei einem Backup gespeichert werden.
# Archiv-Bit = "0": Die Datei wurde (z.B. seit dem letzten Backup) nicht verändert und muss nicht neu archiviert werden.
# Ist das Archiv-Bit auf 0, wird es automatisch vom Betriebssystem auf 1 gesetzt, wenn auf das File schreibend zugegriffen wird.
# Dieses Attribut lässt sich mit dem folgenden Win-Programm ändern:
# attrib.exe meinFile -A (Löscht das Archiv-Bit =0)
# attrib.exe meinFile +A (Setzt das Archiv-Bit =1)
# In Powershell:
set-itemproperty meinFile -Name Attributes -Value "Normal" # → Löscht alle Attribute, auch das Archive-Attribut
set-itemproperty meinFile -Name Attributes -Value "Archive" # → Setzt das Archive-Attribut
[Object]$meineDatei = get-childitem meinText.txt # Objektvariable initialisieren und deklarieren
if($meineDatei.mode -eq "-a----") # → Das Datei-Attribut in der Objekt-Eigenschaft "Mode" ausgewertet
{ # (Mode ist übrigens schreibgeschützt)
# Tu was Gutes
}
Webseite in eine Datei kopieren:
$wc = new-object System.Net.WebClient
$wc.DownloadString('https://juergarnold.ch/index.html') | out-file edu.txt
get-content edu.txt
Eingabe der Pfadnamen für Quellverzeichnis und Zielverzeichnis inklusive Überprüfung.
function validatePath # Pfadsyntax Ueberpruefen
{
$trim = $args[0] -replace " ", "" # Alle Leerschlaege loeschen
if ($trim.Length -eq 0 -or (Test-Path -isValid $args[0]) -eq $False)
{
return $False; # Rueckgabewert ins initDirectory()
}
return $True; # Rueckgabewert ins initDirectory()
}
function initDirectory # Syntax-Check
{
if ((validatePath($args[0])) -eq $False) # Funktionsaufruf validatePath()
{
return $False # Rueckgabewert ins Hauptprogramm
}
if ((Test-Path -path $args[0]) -eq $False) # Existiert Verzeichnis bereits?
{
new-item -path $args[0] -type directory # Verzeichnis erstellen, falls noch nicht vorhanden
write-host " Verzeichnis $args neu erstellt!"
}
else
{
write-host " Verzeichnis $args existiert bereits!"
}
return $args[0] # Rueckgabewert ins Hauptprogramm
}
# ------------------ Hauptprogramm --------------------------------------------------
do # Abfrage nach dem Quellverzeichnis
{
$mySRC = Read-Host "Quellverzeichnis"
$src = $mySRC
$src = initDirectory($src)
} While($src -eq $False)
do # Abfrage nach dem Zielverzeichnis
{
$myDST = Read-Host "Zielverzeichnis"
$dst = $myDST
$dst = initDirectory($dst)
} While($dst -eq $False)
# Kontrollausgabe
write-host "Quellverzeichnis:" $mySRC " Zielverzeichnis:" $myDST
∇ AUFGABEN
- Verzeichnis erstellen A: Verwenden sie für die folgenden Aufgaben die Powershell-ISE und ausschliesslich CmdLets und keine CmdTools:
- Wechseln sie in ihr HomeVerzeichnis und danach in .\Desktop
- Erstellen sie darin das Unterverzeichnis .\Test
- Wechseln sie in das neu erstellte Verzeichnis .\Test
- Setzen sie den Arbeitspfad (aktueller Pfad) auf ihr aktuelles Arbeitsverzeichnis. (Tipp: CurrentDirectory)
- Kreieren sie ein Unterverzeichnis mit dem Namen "Backup". Prüfen sie vorher, ob es nicht bereits existiert.
- Kreieren sie folgende Dateien "a.txt" mit dem Inhalt "Hello World" und "b.txt" mit dem Inhalt "Felix Muster is watching you"
- Erstellen sie im Verzeichnis "Backup" ein Backup von ihrer bestehenden Datei "a.txt". Nennen sie diese Backup-Datei dann a.bck (Tipp: .CopyTo(..))
- Listen sie das aktuelle und alle Unterverzeichnisse in die Datei "c.txt"
- Verschieben sie die Datei "c.txt" in das Verzeichnis .\Backup
- Erstellen sie eine Liste mit allen Prozessen, die das Pattern "win" in Namen tragen. Diese Datei soll d.txt heissen.
- Löschen sie die Datei "d.txt"
- Verschieben sie alle im aktuellen Verzeichnis verbliebenen Text-Dateien in das Verzeichnis .\Backup. (Tipp: Move-Item))
- Verzeichnis erstellen B: Mit diesem Script soll ein Verzeichnis erstellt werden, sofern es nicht bereits existiert.
- Eingabe mit folgendem Begleittext "Bitte Verzeichnisname eingeben"
- Ausgabe, falls Verzeichnis noch nicht existiert: "Das Verzeichnis xxx wurde erstellt"
- Sonst Ausgabe "Das Verzeichnis xxx existiert bereits"
- Datei auswählen und kopieren: Nun sollen sie die in den vorangegangenen Aufgaben erlernten CmdLets in ein Skript einbauen.
Erstellen sie den Powershell-Skript für die folgende Aufgabe:
- Anzeige aller Dateien (keine Verzeichnisse) im aktuellen Verzeichnis
- Auswahl des Filenamens, von dem eine Kopie erstellt werden soll.
- Überprüfung, ob der angegebene Dateiname existiert, sonst Fehlermeldung und erneute Abfrage.
- Bei erfolgreichem Kopieren: Erfolgsmeldung an den Benutzer mit Angabe des Filenamens der Kopie.
- Wiederholung des ganzen Vorgangs für die Erstellung einer weiteren Kopie oder Abbruch (Programm verlassen)
- Dateiarchiv: Erstellen sie ein Powershell-Skript für die folgende Aufgabe:
Variante-1:
- Der Benutzer gibt den Namen einer Datei an, die archiviert werden soll.
- Das Programm prüft, ob die Datei existiert. Wenn Nein wird eine Fehlermeldung ausgegeben und das Programm beendet.
- Der Benutzer gibt das Zielverzeichnis an, in das die Datei archiviert werden soll.
- Das Programm prüft, ob das Zielverzeichnis existiert. Wenn Nein wird eine Fehlermeldung ausgegeben und das Programm beendet.
- Das Programm prüft das Archivbit der Datei. Wenn es gesetzt ist, wird der Kopiervorgang ausgelöst und eine Vollzugsmeldung ausgelöst. Falls das Archivbit nicht gesetzt ist, gibt das Programm die Meldung aus, dass die Datei nicht archiviert werden muss.
- Testen sie das Programm gründlich. Spielen sie alle möglichen Fälle durch, auch den Fall "ArchivBit gesetzt" und "Archivbit nicht gesetzt".
Variante-2:
- Sie möchten ein komplettes Verzeichnis (keine Unterverzeichnisse) archivieren.
- Der Benutzer gibt das Quellverzeichnis an.
- Der Benutzer gibt das Zielverzeichnis an.
- Falls das Zielverzeichnis nicht existiert, muss es zuerst erstellt werden.
- Es werden alle Files vom Quellverzeichnis ins Zielverzeichnis kopiert.
- Zusatzaufgabe1: Setzen sie das Archiv-Attribut der Originaldateien auf "Datei gesichert". (Tipp: Get-ItemProperty und Set-ItemProperty)
- Zusatzaufgabe2: Archivieren sie nur die Dateien, wo das Archiv-Attribut gesetzt ist. Also nur diejenigen Dateien, die schon einmal archiviert
und seither nicht angerührt worden sind.
∇ LÖSUNGEN
- Verzeichnis erstellen A:
#Aufgabe 1:
Set-Location c:\users\felixmuster\desktop
#Aufgabe 2:
New-Item -Path . -name "Test" -ItemType directory
#Aufgabe 3:
Set-Location .\Test
#Aufgabe 4:
[environment]::CurrentDirectory = Get-Location
# Hinweis: [System.Environment]::CurrentDirectory enthält das Working-Directory des PowerShell-Prozesses
# bzw. man kann, wie im obigen Beispiel gezeigt, es darüber setzen.
# Um hingegen das Working-Directory der Konsolen-Session zu lesen oder schreiben, verwendet man
# das CommandLet Get-Location, Set-Location oder $pwd.
#Aufgabe 5:
$myPath = ".\Backup"
if (!(Test-Path $myPath)) {New-Item -Path $myPath -ItemType Directory}
# Hier zum Vergleich eine etwas lesbarere Version:
$myPath = ".\Backup"
if (Test-Path $myPath) # → if ( (Test-Path $myPath) -eq $True )
{
write-host "Verzeichnis existiert bereits"
}
else
{
New-Item -Path $myPath -ItemType Directory
}
#Aufgabe 6:
"Hello World" > a.txt
"Felix Muster is watching you" > b.txt
#Aufgabe 7:
$myFile = Get-Item a.txt
$myFile.CopyTo(".\Backup\a.bck", -1)
#Aufgabe 8:
Get-ChildItem . -recurse > c.txt
# Hinweis: -recurse steht für Rekursiv und bezweckt, dass alle Zweige des Verzeichnisbaums
# erfasst werden.
#Aufgabe 9:
Move-Item c.txt .\Backup
#Aufgabe 10:
Get-Process | Foreach-Object {
if ($_.ProcessName –like "*win*")
{
$_.ProcessName
}
}
#Aufgabe 11:
Remove-Item d.txt
#Aufgabe 12:
$myFileList = get-ChildItem *.txt
$destDir = ".\Backup"
Move-Item $myFileList $destDir
- Verzeichnis erstellen B:
# Programm-Header (arj/3.3.2020)***
set-psdebug -strict
# Variablendeklaration + Initialisierung
[string]$dirname = ""
[string]$pathname = (get-location).path
cls
$dirname = read-host "Bitte Verzeichnisname in $pathname eingeben"
if( (Test-Path -path $dirname) -eq $True)
{
write-host "Das Verzeichnis $dirname existiert bereits."
}
else
{
New-Item -Path . -name $dirname -ItemType directory
write-host "Das Verzeichnis $dirname wurde erstellt."
}
- Datei auswählen und kopieren:
# Programm-Header (ARJ/22.3.2020)***
set-psdebug -strict
# Variablendeklaration + Initialisierung
[string]$meinStandort = Get-Location
[string]$meinDateinamen = ""
[string[]]$meinSeparierterDateinamen # String-Array
[string]$meinBackupnamen = ""
[char]$fortsetzen = "n"
# Hauptprogramm
cls
[environment]::CurrentDirectory = $meinStandort
write-host $meinStandort
do
{
Get-ChildItem -file -name
# Datei auswaehlen
$meinDateinamen = read-Host "Von welcher Datei soll ein Backup erstellt werden?"
#Dateiexistenz ueberpruefen
if(Test-Path -path $meinDateinamen)
{
$meinSeparierterDateinamen = $meinDateinamen.split(".") # Dateinamen [Namen.Extension] in zwei Arrayelemente aufteilen
$meinBackupnamen = $meinSeparierterDateinamen[0] + ".bck" # Neuer Dateinamen zusammensetzen
Copy-Item $meinDateinamen $meinBackupnamen # Kopieren
write-host "$meinBackupnamen wurde erstellt" # Meldung an Benutzer
}
else
{
write-host "$meinDateinamen ist nicht vorhanden"
}
$fortsetzen = read-Host "Sollen weitere Backups erstellt werden? [j/n]"
} while($fortsetzen -eq "j")
write-host "Programm beendet."
- Dateiarchiv:
# Programm-Header (ARJ/22.3.2020)***
set-psdebug -strict
# Variablendeklaration + Initialisierung
[string]$meinStandort = Get-Location
[string]$meinQuellVerzeichnis = ""
[bool]$QuellverzeichnisOK = $False
[string]$meinZielVerzeichnis = ""
[bool]$ZielverzeichnisOK = $False
# Hauptprogramm
cls
[environment]::CurrentDirectory = $meinStandort # Aktueller Standort setzen
# Quellverzeichnis
$meinQuellVerzeichnis = read-Host "Quellverzeichnis, aus dem Dateien archiviert werden sollen"
if(Test-Path -path $meinQuellVerzeichnis) # Pruefen ob Quellverzeichnis vorhanden ist
{
if( ((get-childitem $meinQuellVerzeichnis).count) -eq 0) # Pruefen ob Quellverzeichnis leer ist
{
write-host "Das Verzeichnis $meinQuellVerzeichnis ist leer"
}
else
{
$QuellverzeichnisOK = $True
}
}
else
{
write-host "Das Verzeichnis $meinQuellVerzeichnis ist nicht vorhanden"
}
if($QuellverzeichnisOK) # Wenn Eingabe von Quellverzeichnis erfolgreich war...
{
#Zielverzeichnis
$meinZielVerzeichnis = read-Host "Zielverzeichnis, in das die Dateien archiviert werden sollen"
if(Test-Path -path $meinZielVerzeichnis) # Pruefen ob Zielverzeichnis vorhanden ist
{
if( ((get-childitem $meinZielVerzeichnis).count) -eq 0) # Pruefen ob Zielverzeichnis leer ist
{
$ZielverzeichnisOK = $True
}
else
{
write-host "Das Verzeichnis $meinQuellVerzeichnis ist nicht leer. Bitte ein anderes Zielverzeichnis waehlen."
}
}
else
{
$ZielverzeichnisOK = $True
New-Item -Path $meinZielVerzeichnis -ItemType Directory | Out-Null # Mit Out-Null wird die Ausgabe von New-Item unterdrueckt
}
if($ZielverzeichnisOK) # Wenn Eingabe von Zielverzeichnis erfolgreich war, soll nun kopiert werden
{
$meineDateiliste = get-ChildItem $meinQuellVerzeichnis
foreach($meineDatei in $meineDateiliste) {
if($meineDatei.mode -eq "-a----") # Nur Dateien mit gesetztem Archiv-Bit kopieren
{
$meineDatei.CopyTo($meinZielVerzeichnis + "\" + ($meineDatei.Name), -1) | Out-Null
# Zielpfad/Dateiname richtig zusammensetzen!
}
}
set-itemproperty $meinZielVerzeichnis\*.* -Name Attributes -Value "Normal"
write-host "Alle Dateien aus $meinQuellVerzeichnis wurden erfolgreich nach $meinZielVerzeichnis kopiert."
}
}
# Hinweis zum Dateiattribut "Archive":
# In den Dateieigenschaften findet man unter Allgemein/Erweitert das Archive-Attribut.
# Dieses Attribut lässt sich mit dem folgenden Win-Programm ändern:
# attrib.exe meinFile -A (Löscht das Archiv-Bit =0)
# attrib.exe meinFile +A (Setzt das Archiv-Bit =1)
# Ist das Archiv-Bit auf 0, wird es automatisch vom Betriebssystem auf 1 gesetzt, wenn auf das File schreibend zugegriffen wird.
# Das Archiv-Bit kann z.B. von einer Backup-SW ausgewertet werden.
# In Powershell:
# set-itemproperty meinFile -Name Attributes -Value "Normal" → Löscht alle Attribute, auch das Archive-Attribut
# set-itemproperty meinFile -Name Attributes -Value "Archive" → Setzt das Archive-Attribut
# ($meineDatei.mode -eq "-a----") → Das Datei-Attribut in der Objekt-Eigenschaft "Mode" ausgewertet (Mode ist schreibgeschützt)
7.3 Lokale Benutzer und Gruppen verwalten
Die folgenden CmdLets helfen bei der Automatisierung des lokalen User-Managements. Dies ist insbesondere dann interessant, wenn man die Vorgaben wie Benutzername, Passworte usw. aus einer EXCEL-Datei bezieht.
Wie man mit EXCEL-Dateien arbeitet, ist im nachfolgenden Kapitel "Arbeiten mit COM-Objekten" beschrieben.
(Hinweis: Für das Anlegen eines Active-Directory-Users siehe New-ADUser und Set-ADUser. Wird hier aber nicht weiter behandelt!)
Get-Command -Module Microsoft.PowerShell.LocalAccounts # Sämtliche CmdLets für LocalAccounts
Get-LocalUser A* | select * # Zeigt alle User mit dem Startbuchstaben "A" an
Get-LocalGroup # Zeigt alle Gruppen an
Neues Benutzerkonto erstellen, ändern und löschen
Angaben, mit denen sich die Eigenschaften eines neu zu erstellenden Kontos festlegen lassen sind:
- AccountExpires → DateTime
- AccountNeverExpires
- Description → String
- Disabled
- FullName → String
- Name → String (Pflichtangabe)
- Password → SecureString (Pflichtangabe)
- NoPassword (Alternative Pflichtangabe zum obigen Password, wenn auf ein Passwort verzichtet wird)
- PasswordNeverExpires
- UserMayNotChangePassword
# Interaktive-Variante
[System.Security.SecureString]$meinPasswort = Read-Host -AsSecureString
New-LocalUser -Name "FelixMuster" -Password $meinPasswort -FullName "Felix Muster" -Description "Musterknabe"
# Batchverarbeitungs-Variante
[System.Security.SecureString]$meinPasswort = ConvertTo-SecureString -String "Pa$$w0rd" -AsPlainText -Force
New-LocalUser -Name FelixMuster -Password $meinPasswort -FullName "Felix Muster" -Description "Musterknabe"
Set-LocalUser -Name FelixMuster -Description "Musterknabe" -AccountExpires "31.12.2030" # Nachträgliche Angaben
Rename-LocalUser -Name FelixMuster -NewName FelixKuster # Benutzername umbenennen
Remove-LocalUser -Name FelixMuster # Eintrag wieder löschen
Neue Gruppe erstellen, ergänzen und löschen
Get-LocalGroup # Zeigt alle Gruppen an
Get-LocalGroupMember -Name Administratoren # Alle in Administratoren enthaltene Benutzer/Gruppen
New-LocalGroup -Name Verkauf -Description "Abteilung Verkauf" # Erstellt neue Gruppe für die Abteilung Verkauf
Add-LocalGroupMember -Name Verkauf -Member FelixMuster # Fügt Felix Muster der Gruppe Verkauf hinzu
Remove-LocalGroupMember -Name Verkauf -Member FelixMuster # Entfernt Felix Muster aus der Gruppe Verkauf
Remove-LocalGroup -Name Verkauf # Löscht die Gruppe Verkauf
# Anstelle von Benutzernamen kann man bei Add-LocalGroupMember und Remove-LocalGroupMember
# für Member auch eine Gruppe angeben.
7.4 Arbeiten mit COM-Objekten
Das Component Object Model (COM) ist eine von Microsoft entwickelte Technik zur Interprozesskommunikation unter Windows.
COM-Komponenten können sowohl in Form von Laufzeitmodulen (DLLs) als auch als ausführbare Programme umgesetzt sein. COM soll eine leichte Wiederverwendung von bereits geschriebenem Programmcode ermöglichen,
zum Teil auch über Betriebssystemgrenzen hinweg.
Als Beispiel sollen Automatisierungsmöglichkeiten von Office, speziell Excel und Webseitenabfragen mit Internetexplorer aufgezeigt werden.
Excel:
# Bestehendes Workbook öffnen und lesen(Schritt für Schritt):
$myExcel = New-Object -comobject Excel.Application # Excel-Objekt erzeugen
$myWorkBook = $myExcel.Workbooks.Open("C:\...\meineDatei.xlsx") # Workbook öffnen
$myWorkSheet = $myWorkBook.sheets.item("Tabelle1") # Worksheet/Tabelle öffnen
$myExcel.Visible = $True # EXCEL-Tabellen anzeigen, falls gewünscht
[string]$meinText1 = $myWorkSheet.Cells.Item(1,1).Text # Text in Zelle A1 lesen
[string]$meinText2 = $myWorkSheet.Cells.Item(2,1).Text # Text in Zelle A2 lesen
$myExcel.Quit() # Excel schliessen
Remove-Variable myWorkSheet, myWorkBook, myExcel # Variablen löschen
# Neues Workbook erstellen und befüllen (Schritt für Schritt):
$myExcel = New-Object -comobject Excel.Application # Excel-Objekt erzeugen
$myWorkBook = $myExcel.Workbooks.Add() # Neues Workbook erzeugen
$myWorkSheet = $myWorkBook.Worksheets.Item(1) # Neues Worksheet/Tabelle erzeugen
$myWorkSheet.Cells.Item(1,1) = "Hello World" # Zellen füllen
$myWorkSheet.Cells.Item(2,1) = "2+4" # Bsp.: Textzeile
$myWorkSheet.Cells.Item(3,1) = 2+4 # Bsp.: Integer
$myExcel.Visible = $True # EXCEL-Tabellen anzeigen, falls gewünscht
$myWorkBook.SaveAs(".\test.xlsx") # Excelsheet abspeichern...
(...bei relativem Pfad in Dokumentenordner)
$myExcel.Quit() # Excel schliessen
Remove-Variable myWorkSheet, myWorkbook, myExcel # Variablen löschen
# Hilfe erhalten
$myExcel | Get-member # Eigenschaften / Methoden des Excel-Objekts
$myExcel.Workbooks | Get-Member # Eigenschaften / Methoden von Workbooks
$myExcel.Workbooks | Select-Object -Property name, path, author # Überprüfen, ob Workbooks geöffnet ist
Internet-Explorer:
# InternetExplorer-Objekt erzeugen:
$myIE = New-Object -comobject InternetExplorer.Application
# Eigenschaften und Methoden des InternetExplorer-Objekts anzeigen:
$myIE | Get-member
# InternetExplorer positionieren:
$myIE.top = 20
$myIE.width = 1800
$myIE.height = 1600
$myIE.Left = 20;
# InternetExplorer anzeigen:
$myIE.Visible = $True
# Webseite aufrufen:
$myIE.Navigate("edu.juergarnold.ch")
∇ AUFGABE
Lokalen Benutzer mit Angaben aus einer EXCEL-Datei erstellen:
- Erstellen sie eine EXCEL-Datei mit dem Namen "Benutzer.xlsx".
- Tragen sie in Tabelle1 Feld A1 den Namen "FelixMuster" ein.
- Tragen sie in Tabelle1 Feld A2 das Passwort "geheim" ein.
- Benennen sie die Tabelle anstatt wie bisher "Tabelle1" neu "Benutzer"
- Erstellen sie einen Powershell-Skript, der die beiden Felder A1 und A2 aus der Tabelle "Benutzer.xlsx" ausliest und mit write-host anzeigt.
- Erstellen sie einen Powershell-Skript, der aus den Angaben in Tabelle "Benutzer.xlsx" einen lokalen Windows-User erstellt.
- Prüfen sie ihr Programm gründlich.
∇ LÖSUNG
Lokalen Benutzer mit Angaben aus einer EXCEL-Datei erstellen:
# Zweck : Benutzer gemaess Excel-Tabelleneintrag erstellen
# Autor/Datum : Arj/April 2020
# Voraussetzung: Sie haben in E:\test eine Exceldatei mit dem Namen Benutzertabelle.xlsx
# Die Tabelle besitzt ein Blatt (Sheet) mit dem Namen Benutzer
# In der Zelle A1 ist der Benutzername eingetragen
# In der Zelle A2 ist das Passwort eingetragen
# Falls sie von diesen Angaben abweichen, muss das im folgenden Skript entsprechend angepasst werden.
set-strictmode -version latest # Must-have!
[string]$thePath = "E:\test\Benutzertabelle.xlsx"
[string]$theTable = "Benutzer"
[string]$myUsername = ""
[string]$myPassword = ""
[System.Security.SecureString]$mySecurePassword
[object]$myExcel
[object]$myWorkbook
[object]$myTable
cls # Konsolenanzeige leeren
$myExcel = New-Object -ComObject excel.application # Excel starten
$myWorkbook = $myExcel.Workbooks.Open($thePath) # Excel-Dokument oeffnen
$myTable =$myWorkbook.Worksheets.Item($theTable) # Tabelle waehlen
$myUsername = $myTable.Cells.Item(1,1).Text # Ausgabe des Texts in Excel-Zelle A1
$myPassword = $myTable.Cells.Item(2,1).Text # Ausgabe des Texts in Excel-Zelle A2
$myExcel.Quit() # Beendung von Excel
Remove-Variable myTable, myWorkbook, myExcel # Variablen wieder entfernen
write-host "Es wird der Benutzer ""$myUsername"" mit dem Passwort ""$myPassword"" erstellt..."
$mySecurePassword = ConvertTo-SecureString -String $myPassword -AsPlainText -Force
New-LocalUser -Name $myUsername -Password $mySecurePassword
# Am Schluss nicht vergessen, den neu erstellten Benutzer wieder zu loeschen: cmd-Tool → lusrmgr.msc
7.5 Arbeiten mit Eventlogs
# Die drei letzten Einträge im System-Eventlog formatiert auslesen:
get-eventlog system -newest 3 | format-list
get-winevent -logname system -computername 127.0.0.1 -maxevents 3 | format-list
# Ein Event in den Application-Eventlog eintragen:
# (Falls noch nicht geschehen, die neue Event-Quelle einmalig registrieren)
New-EventLog -LogName Application -Source myApp # Neue Event-Quelle registrieren
Write-EventLog -logname "Application" `
-computername $env:COMPUTERNAME `
-source "myApp" `
-eventID 9999 `
-entrytype Information `
-message "Hello World"
# Hinweis:
# -computername [computername] ermöglicht auch die Abfrage über das Netzwerk
# $env:COMPUTERNAME ist der eigene Computername 127.0.0.1.
# In diesem Fall könnte -computername auch ganz weggelassen werden
∇ AUFGABEN
- Laufende Prozesse zählen: Es sollen mit diesem Skript alle auf dem PC laufenden Prozesse zusammengezählt und das Resultat in der folgenden Art ausgegeben werden:
"Aktuell sind xxx Prozesse am laufen"
Tipp: Get-Process
- Höchste Prozesszeit anzeigen: Ausgehend von der vorangegangenen Aufgabe soll nun der Prozess mit der grössten TotalProcessorTime angezeigt werden.
Hinweis: Probieren sie die ProzessObjekt-Property TotalProcessorTime zuerst aus. Sie werden sehen, dass verschiedene Zeitangaben erscheinen.
Welche ist für diese Aufgabe am sinnvollsten? Aufruf: TotalProcessorTime.yyyyyyy
- Prozessverwaltung: Erstellen sie ein Skript für die Prozessverwaltung. Die folgenden Aktionen sollen über ein Benutzermenü angeboten werden:
- Anzeige aller laufenden Prozesse
- Suche nach bestimmten Prozessen
- Statusanzeige bzw. auf Wunsch Anzeige weiterer Eigenschaften eines bestimmten Prozesses
- Anzeige des gesamten RAM-Verbrauchs aller Prozesse
- Anzeige der CPU-Auslastung
- Zusatzaufgabe: Logfile über RAM-Verbrauch und CPU-Auslastung erstellen
- Eventlog anzeigen: Um zu zeigen, dass Powershell fü Systemtechniker wirklich hilfreich sein kann, lesen wir nun das SYSTEM-Eventlog aus:
- Angezeigt werden soll: LogName=System und EntryType=Error
- Eingabe: Startdatum, ab welchem die Einträge gezeigt werden
- Eingabe: Enddatum, bis zu welchem die Einträge gezeigt werden
- Tipp: Get-EventLog. Start- und Enddatumseingabe sind eine kleine Knacknuss ;-)
∇ LÖSUNGEN
- Laufende Prozesse zählen:
# Programm-Header ***
set-psdebug -strict
# Variablendeklaration und Initialisierung
[int64]$i = 0
cls
Get-Process | Foreach-Object{
$i = $i + 1
}
write-host "Aktuell sind $i Prozesse am laufen"
- Höchste Prozesszeit anzeigen:
# Programm-Header ***
set-psdebug -strict
# Variablendeklaration + Initialisierung
[long]$maxProcTime = 0
[string]$procName = ""
cls
Get-Process | Foreach-Object {
if($_.TotalProcessorTime -ge $maxProcTime)
{
$maxProcTime = $_.TotalProcessorTime.Ticks
$procName = $_.ProcessName
}
}
write-host "Der Prozess $procName weist mit $maxProcTime die hoechste TotalProcessorTime aus."
- Prozessverwaltung: Keine Musterlösung vorhanden.
- Eventlog anzeigen:
# Programmheader (arj/3.3.2020)
set-strictmode -version latest
# Variablen-Deklaration & Initialisierung
[string]$readDate = ""
[datetime]$startDate = get-date
[datetime]$EndDate = get-date
[bool]$nok = $True
cls
do #Startdatum einlesen
{
$readDate = Read-Host "Startdatum (ddMMyyyy)"
try
{
$startDate = [datetime]::parseExact($readDate,"ddMMyyyy",$null)
$nok = $False
}
catch
{
write-host "Bitte korrektes Format!"
$nok = $True
}
}while($nok)
do #Enddatum einlesen
{
$readDate = Read-Host "Enddatum (ddMMyyyy)"
try
{
$EndDate = [datetime]::parseExact($readDate,"ddMMyyyy",$null)
$nok = $False
}
catch
{
write-host "Bitte korrektes Format!"
$nok = $True
}
}while($nok)
# Eventlog auslesen
Get-EventLog -LogName System -EntryType Error -After $StartDate -Before $EndDate
7.6 Arbeiten in der Registry
Die Windows-Registrierungsdatenbank (Registry) ist die zentrale hierarchische Konfigurationsdatenbank des Betriebssystems Windows.
Hier werden sowohl Informationen zu Windows selbst als auch Informationen zu Programmen gespeichert.
Die Registry steht Powershell wie ein normales Laufwerk zur Verfügung. Darum kommen ähnliche CmdLets und Funktionen zur Anwedung
wie beim Arbeiten mit Dateien. Registry-Schlüssel (HKEY_ = Handle to a Key_) lassen sich mit Get-ChildItem anzeigen,
deren Inhalte mit Get-ChildItemProperty.
# Registry-Schlüssel und Properties in "HKEY_LOCAL_MACHINE\Software" anzeigen:
Get-ChildItem HKLM:\Software
# Properties anzeigen: (Hier der Autostart-Schlüssel)
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Run
# Neuer Registry-Schlüssel in "HKEY_LOCAL_MACHINE\Software" anlegen:
New-Item -path "HKLM:\Software" -Name "Testkey"
# Neuer Eintrag erstellen:
New-ItemProperty -literalpath "HKLM:\Software\Testkey" -Name "Testkey" -Value "Test CRM" -type String
# Eintrag entfernen:
Remove-ItemProperty -literalpath "HKLM:\Software\Testkey" -Name "Testkey"
# Registry-Schlüssel entfernen:
Remove-Item -path "HKLM:\Software\Testkey"
7.7 Arbeiten mit Webformularen
# Mit Invoke-WebRequest ein Formular aufrufen und Formular mit SessionKey merken:
$myWebForm=Invoke-WebRequest http://edu.juergarnold.ch/fach_it/wpssummary/testForm.html -SessionVariable mySession
# Im Formular "berechnung" das Feld "resultat" beschreiben:
$myWebForm.Forms["berechnung"].Fields["resultat"] = "2"
# Formularinhalt übermitteln und Formularaktion auslösen:
Invoke-WebRequest -Method POST `
-URI ("http://edu.juergarnold.ch/fach_it/wpssummary/" + $myWebForm.Forms["berechnung"].action) `
-Body $myWebForm.Forms["berechnung"].Fields -WebSession $mySession
7.8 Arbeiten mit .net-Objekten - Erstellen einer grafischen Benutzereingabe
# Benötigte .NET-Frameworkklassen laden:
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
# Instanz einer Form-Klasse erstellen:
$form = New-Object System.Windows.Forms.Form
# Form-Objekt anpassen:
$form.Text = 'FORMULAR'
$form.Size = New-Object System.Drawing.Size(400,300)
$form.StartPosition = 'CenterScreen'
# OK-Button kreiern, anpassen und in das Formular einfügen:
$OKButton = New-Object System.Windows.Forms.Button
$OKButton.Location = New-Object System.Drawing.Point(75,120)
$OKButton.Size = New-Object System.Drawing.Size(75,23)
$OKButton.Text = 'OK'
$OKButton.DialogResult = [System.Windows.Forms.DialogResult]::OK
$form.AcceptButton = $OKButton
$form.Controls.Add($OKButton)
# Cancel-Button kreiern, anpassen und in das Formular einfügen:
$CancelButton = New-Object System.Windows.Forms.Button
$CancelButton.Location = New-Object System.Drawing.Point(150,120)
$CancelButton.Size = New-Object System.Drawing.Size(75,23)
$CancelButton.Text = 'Cancel'
$CancelButton.DialogResult = [System.Windows.Forms.DialogResult]::Cancel
$form.CancelButton = $CancelButton
$form.Controls.Add($CancelButton)
# Beschriftung kreiern, anpassen und in das Formular einfügen:
$label = New-Object System.Windows.Forms.Label
$label.Location = New-Object System.Drawing.Point(10,20)
$label.Size = New-Object System.Drawing.Size(280,20)
$label.Text = 'Bitte Text eingeben:'
$form.Controls.Add($label)
# Texteingabezeile kreiern, anpassen und in das Formular einfügen:
$textBox = New-Object System.Windows.Forms.TextBox
$textBox.Location = New-Object System.Drawing.Point(10,40)
$textBox.Size = New-Object System.Drawing.Size(260,20)
$form.Controls.Add($textBox)
# Topmost-Eigenschaft auf $True setzen, um das Öffnen des Fensters über anderen geöffneten Fenstern und Dialogfeldern zu erzwingen:
$form.Topmost = $true
# Formular aktivieren und Fokus auf das Textfeld setzen:
$form.Add_Shown({$textBox.Select()})
# Formular anzeigen lassen:
$result = $form.ShowDialog()
# Der Code im If-Block weist Windows an, wie mit dem Formular verfahren werden soll,
# nachdem der Benutzer einen Text eingegeben und die Schaltfläche OK oder Eingabetaste gedrückt hat:
if ($result -eq [System.Windows.Forms.DialogResult]::OK)
{
$x = $textBox.Text
}
∇ AUFGABEN
Taschenrechner: Bis jetzt haben wir immer textbasierte Benutzerschnittstellen erstellt. Das geht aber auch grafisch mit .net-Objekten.
Erstellen sie einen Rechner mit robuster grafischer Eingabe, wo sie einen Winkel (Dezimalzahl) im Gradmass eingeben und davon den Sinuswert (Dezimalzahl) berechnen. Nutzen Sie die entspprechende Library.
(Beachten sie, dass bei der Sinusfunktion der Winkel im Bogenmass eingegebn wird. Dabei gilt: 360° entsprechen 2π. Der Sinus ist definiert von -∞ bis + ∞)
∇ LÖSUNGEN
Keine Musterlösung vorhanden.
8. Summary
Wenn Sie alle bisherigen Aufgaben gelöst haben, sollten Sie nun in der Lage sein, mit Powershell...
- ...verschiedene nützliche CMD-Lets auszuführen und Ausgabe in Dateien umzuleiten...
- ...Aufgaben in eigenen Funktionen zusammenzufassen...
- ...Mathematikfunktionen und Stringmanipulationen auszuführen...
- ...Datum und Uhrzeit in Abläufe einzubeziehen...
- ...laufende Prozesse zu untersuchen (z.B. Prozesslaufzeit)...
- ...Eventlogs auszulesen...
- ...Dateien und Verzeichnisse zu erstellen, archivieren und zu löschen...
- ...lokale Benutzer einzurichten...
- ...Excel-Dateien zu lesen und zu schreiben...
- ...eine robuste Benutzereingabe zu erstellen...
- ...sauber strukturierte, lesbare und styleguideskonforme (Datentypen, Namenskonzepte, Zeileneinrückung, Kommentare etc.) Skripte zu erstellen und dabei das formelle Testen nicht zu vergessen.
Wenn Sie Powershell-Experte werden wollen, bleiben Sie dran! Wir haben bisher erst ein bisschen an der "Oberfläche gekrazt" ;-)
Falls Sie die obigen Minimalziele noch nicht erreicht haben, wiederholen sie die Übungen, ändern oder ergänzen sie diese oder fragen sie beim Dozenten nach weiteren Aufgaben.
∇ AUFGABEN
- Datumsarithmetik:
- Lesen Sie zwei Daten ein und berechnen Sie die Differenz in Monaten.
- Lesen Sie ein Datum ein und bestimmen Sie, ob es in diesem Jahr einen 29. Februar gibt (=Schaltjahr).
- Wordprocessing:
- Lesen Sie einen Satz ein und zählen Sie die Anzahl Worte.
- Suchen Sie ihr gesamtes "~\"-Verzeichnis nach Dateien ab, die die Endung ".html" enthalten.
- Dateioperationen: Sie erhalten in einem ZIP-File verschiedene Bilddateien von verschiedenen Szenen: AlleSzenen.zip
Entpacken sie diese. Das Programm soll auf die entpackte ZIP-Datei angewendet werden und folgende Funktionen nacheinander ausführen:
- Durchsuchen der Dateinamen nach dem Sonderzeichen "#" und ersetzen durch "_".
- Löschen aller Dateien, die "Kopie" im Dateinamen enthalten.
- Verschieben aller Dateien derselben Szene in einen eigenen Szene-Ordner.
- Bei allen Dateien die Eigenschaft "CreationTime" auf die aktuelle Zeit setzen.
∇ LÖSUNGEN
#******************************************************************************
# ARJ, DATUM: MAR-2021
#******************************************************************************
set-strictmode -version latest # Codierungsregeln verschärfen
#******************* Variablen-Deklaration & Initialisierung ******************
[DateTime]$Datum_A = Get-Date "1.1.1970" # Verwendet in Aufgabe 1.
[DateTime]$Datum_B = Get-Date "1.1.1970" # Verwendet in Aufgabe 1.
[int]$Datum_Diff = 0 # Verwendet in Aufgabe 1.
[string]$myText = "" # Verwendet in Aufgabe 2.
[int]$NumberOfWords = 0 # Verwendet in Aufgabe 2.
[Object]$myFiles # Verwendet in Aufgabe 3.
[Object]$File # Verwendet in Aufgabe 3.
[string]$newFilename = "" # Verwendet in Aufgabe 3.
#******************* HIER BEGINNT PROGRAMM 1. *******************************
# 1-1. Lesen Sie zwei Daten ein und berechnen Sie die Differenz in Monaten.
# 1-2. Lesen Sie ein Datum ein und bestimmen Sie, ob es in diesem Jahr einen 29. Februar gibt.
# Somit wäre das Jahr ein Schaltjahr.
cls
# *** Datum für Aufgabe 1-1 und 1-2 eingeben:
$Datum_A = Read-Host "Bitte erstes Datum im Format dd.mm.yyyy eingeben"
$Datum_A = Get-Date -Format "dd.MM.yyy" $Datum_A
$Datum_B = Read-Host "Bitte zweites Datum im Format dd.mm.yyyy eingeben"
$Datum_B = Get-Date -Format "dd.MM.yyy" $Datum_B
# *** Aufgabe 1-1.: (Hinweis Anzahl Jahre mitberücksichtigen)
$Datum_Diff = ($Datum_B.year - $Datum_A.year) * 12 + ($Datum_B.month - $Datum_A.month)
Write-Host "Anzahl Monate inkl. angebrochenen: $Datum_Diff"
# *** Aufgabe 1-2.:
if( ($Datum_A.year % 4 -eq 0) -and ($Datum_A.year % 100 -ne 0) -or ($Datum_A.year % 400 -eq 0) ) {
Write-Host "Der Februar im Jahr $myYear zaehlt 29 Tage!"
}
#******************* HIER BEGINNT PROGRAMM 2. *******************************
# 2-1. Lesen Sie einen Satz ein und zählen Sie die Anzahl Worte.
# 2-2. Suchen Sie ihr gesamtes "~\"-Verzeichnis nach Dateien ab, die die Endung ".html" enthalten.
cls
# *** Aufgabe 2-1.:
$myText = Read-Host "Bitte eine Textzeile eingeben"
$NumberOfWords = ($myText | Measure-Object -Word).words
write-host "Der Text ""$myText"" enthält $NumberOfWords Wörter."
# *** Aufgabe 2-2.:
$myFileList = get-ChildItem ~ -recurse # Zeigt alle Verzeichnisse ab Homeverzeichnis an
foreach($myFile in $myFileList) {
if( ($myFile.PSISContainer -eq $False) -and ($myFile.Name).Contains(".html") ) {
Write-Host $myFile.Name
}
}
#******************* HIER BEGINNT PROGRAMM 3. *******************************
# 3-1. Durchsuchen der Dateinamen nach dem Sonderzeichen "#" und ersetzen durch "_".
# 3-2. Löschen aller Dateien, die "Kopie" im Dateinamen enthalten.
# 3-3. Verschieben aller Dateien derselben Szene in einen eigenen Szene-Ordner.
# 3-4. Bei allen Dateien die Eigenschaft "CreationTime" auf die aktuelle Zeit setzen.
Set-Location C:\Users\FelixMuster\Desktop\M404\AlleSzenen
cls
# *** Aufgabe 3-1.:
$myFiles = Get-ChildItem
if($myFiles -ne $NULL) {
foreach($File in $myFiles) {
$newFilename = $File -replace "#","_"
if ((Test-Path $newFilename) -eq $False) {
Rename-Item $File $newFilename
}
}
}
# *** Aufgabe 3-2.:
$myFiles = Get-ChildItem
if($myFiles -ne $NULL) {
foreach($File in $myFiles) {
if($File -match "Kopie") {
$File.Delete()
}
}
}
# *** Aufgabe 3-3.:
$myFiles = Get-ChildItem
if($myFiles -ne $NULL) {
if ((Test-Path Szene_2) -eq $False) {
New-Item -ItemType Directory -Name Szene_2
}
foreach($File in $myFiles) {
if($File -match "Szene_2") {
Move-Item -Path $File -Destination Szene_2
}
}
}
# *** Aufgabe 3-4.:
$myFiles = Get-ChildItem
if($myFiles -ne $NULL) {
foreach($File in $myFiles) {
(Get-Item $File).CreationTime = [DateTime]::Now
(Get-Item $File).LastAccessTime = [DateTime]::Now
(Get-Item $File).LastWriteTime = [DateTime]::Now
}
}