1 Einleitung
   Beispiel 1: Auslesen und Verändern des Arbeitsverzeichnisses
   Beispiel 2: Ausgabe an einen festdefinierten Pfad
   Beispiel 3: Ausgabe an den Pfad, in dem das Skript selbst gespeichert ist
             1.1 spezielle Pfade
                   1.1.1 wo sind "spezielle Pfade" hinterlegt
                            1.1.1.1 automatic Variables
                                        Beispiel 1a: Liste der Pfadvariablen unter den "automatic variables"
                                        Beispiel 1b: Werte der Pfadvariablen
                                        Beispiel 2: Provider des aktuellen Pfades bestimmen
                            1.1.1.2 Umgebungsvariablen - Environment Variables
                                        Beispiel 1: Anzeige der Umgebungsvariablen
                                        Beispiel 2: Setzen/ Ändern von Umgebungsvariablen
                                        Beispiel 3: Auslesen von Umgebungsvariablen
                                        Beispiel 4: Verändern der Umgebungsvariablen "Path"
                            1.1.1.3 GetFolderpath - Specialfolder
                                        Beispiel 1: Anzeige der Specialfolder mit Pfad
                                        Beispiel 2: Zuweisen eines Specialfolder-Pfades zu einer Variablen
                   1.1.2 das aktuelle Arbeitsverzeichnis und das eigene Skript
                            Beispiel 1a: Ändern und Abfragen des aktuellen Powershellverzeichnisses
                            Beispiel 1b: Ändern und Abfragen des aktuellen .Net Verzeichnisses
                            Beispiel 1c: Ändern des Arbeitsverzeichnisses und Zurücksetzen
                            Beispiel 2: Ermitteln des eigenen Skriptnamens mit Pfad
                   1.1.3 Skriptausgaben in Dateien unter bestimmten Pfaden ausgeben
                            Beispiel 1: Ausgabepfad neben das Skript setzen (Powershell V2.0)
                            Beispiel 2: Ausgabepfad neben das Skript setzen (Powershell V2.0)
                            Beispiel 3: Ausgabepfad neben das Skript setzen (Powershell V2.0)
             1.2 cmdlets zur Pfadbearbeitung
                   Beispiel 1: Anzeige der cmdlets zur Pfadbearbeitung
                   Beispiel 2: zentrales Logverzeichnis oberhalb des temp-Verzeichnisses erstellen, in dem jedes Skript ein spezifisches Logfile ablegt
             1.3 Pfade mit Sonderzeichen
                   Beispiel 1: Navigieren zu einem Pfad mit dem Parameter "-Literalpath"
                   Beispiel 2a: Workaround zum cmdlet "get-acl", das kein -Literalpath enthält
                   Beispiel 2b: Workaround mit dem cmdlet New-PSdrive für das cmdlet "get-acl", das kein -Literalpath enthält
             1.4 Interaktive Pfadeingabe
                   1.4.1 Pfadeingaben über die Eingabeaufforderung
                            Beispiel 1: Pfadeingabe durch Read-Host mit Überprüfung
                   1.4.2 Pfadeingabe mit Dialogfeldern
                            Beispiel 1: Auswählen eines Dateipfades mit der Klasse OpenfileDialog
                            Beispiel 2: Auswählen eines Dateipfades mit der Klasse SavfeFiledialog


1 Einleitung

Eine der ersten Aktionen in einem Skript, das sich Objekten im Filesystem benutzt um beispielsweise Dateien zu schreiben oder zu lesen, ist der Wechsel in das Verzeichnis, in dem irgendetwas unternommen werden soll. Entweder bewegt man dazu das aktuelle Arbeitsverzeichnis der Powershellsession gleich komplett zum Ziel, sowie man das aus der commandshell (cmd.exe "cd /d c:\temp") gewöhnt ist, oder gibt seinen Powershellbefehlen den richtigen Pfad jedesmal mit.

 

Beispiel 1: Auslesen und Verändern des Arbeitsverzeichnisses

Clear-Host
Set-StrictMode -Version "2.0"

$CurrentWorkingLocation = $(Get-Location).Path

$ChangeWorkingLocation = Set-Location C:\temp
$ChangeWorkingLocation = Set-Location 'HKCU:\Control Panel'

Aufregend an Set-Location und Get-Location ist höchstens die Tatsache, dass man damit nicht nur ins Filesystem, sondern auch in die Registry und in andere Quellen hineingreifen kann, sofern die Provider auf dem System installiert sind. (siehe den letzten der 3 folgenden Links)

Liegt der Zielpfad bereits vor, so ist das Navigieren zu dem Pfad natürlich einfach. Muss man den Zielpfad erst neu konstruieren, so helfen eine Reihe von cmdlets weiter. Im nächsten Unterkapitel 1.1 spezielle Pfade untersuche ich, wo besondere, vom Betriebssystem oder von der Powershell selbst vorgegebene, Pfade liegen und wie sie ausgelesen werden können.

 

Beispiel 2: Ausgabe an einen festdefinierten Pfad

Clear-Host
Set-StrictMode -Version "2.0"

$FilePath = "C:\Temp\Einleitung.log"
Out-File -FilePath "$FilePath" -InputObject "www.powershellpraxis.de"

Im Kapitel 1.2 Powershellcmdlets zur Pfadbearbeitung werden die cmdlets untersucht, die man benötigt, wenn ein solcher Pfad noch nicht, oder nur teilweise bekannt ist.

 

Beispiel 3: Ausgabe an den Pfad, in dem das Skript selbst gespeichert ist

Das Skript erzeugt im Skriptverzeichnis bei jedem Lauf eine neue Logdatei, die das aktuelle Datum im Dateinamen trägt.

#Requires -Version 3.0

Clear-Host
Set-StrictMode -Version "2.0"

$ThisScriptParentPath = $PSScriptRoot
If([String]::IsNullOrEmpty($ThisScriptParentPath) -eq $False){
   $Date = ([System.DateTime]::Now).ToString("yyyyMMdd")
   $FilePath = "$ThisScriptParentPath\Einleitung_$Date.log"
   Out-File -FilePath "$FilePath" -InputObject "www.powershellpraxis.de"
   Write-Host "Die Datei wurde unter $FilePath gespeichert"
}Else{
   Write-Host " Bitte zuerst das Skript speichern"

Die Automaticvariable $PSScriptRoot, die den Speicherpfad des Skripts enthält, ist uneingeschränkt erst ab Powershell 3.0 einsetzbar. Für Powershell 2.0 Skripte kann man anstelle dieser Variable auch den Ausdruck

$ThisScriptParentPath = Split-Path $($MyInvocation.InvocationName) -Parent

benutzen. Allerdings nur solange er in keiner Funktion gekapselt ist. Näheres dazu sowie zur Variable "$PSScriptRoot" in Kapitel 1.1.2 das aktuelle Arbeitsverzeichnis und das eigene Skript

 

1.1 spezielle Pfade

Zwei Tipps, die je nach Anwendungsfall nützlich sein können:

  • Wenn im Skript das aktuelle Arbeitsverzeichnis verändert wird, so merkt euch vor der Änderung dieses Verzeichnis und setzt das Arbeitsverzeichnis am Ende des Skripts wieder auf die Ausgangsposition - siehe Beispiel 1c unter 1.1.2 das aktuelle Arbeitsverzeichnis . Für den Skriptbenutzer ist das einfach angenehmer.
  •  

Beispiel 1: Speichern und Wiederherstellen des ArbeitsverzeichnissPfads

Clear-Host
Set-StrictMode -Version "2.0"

$OriginalWorkingLocation = Get-Location

#$OriginalWorkingLocation = $pwd

$NewWorkingLocation = "c:\temp\homes\"
Set-Location $NewWorkingLocation
#...code
Set-Location $OriginalWorkingLocation

Get-Location und $pwd können analog verwendet werden

  • Arbeitet möglichst mit den in Windows, .Net oder in der Powershell vordefinierten Pfaden. Dadurch erreicht ihr, dass euer Skript kompatibler über verschiedene Betriebssystemversionen, Architekturversionen (32/ 64 bit) und Sprachversionen ist, als mit selbst zusammengesetzten Pfaden, die vielleicht nur auf eurem Entwicklerrechner existieren. Um diese vordefinierten Pfade dreht sich das gesamte folgende Kapitel 1 mit seinen Unterkapiteln
  •  

1.1.1 wo sind "spezielle Pfade" hinterlegt

Es gibt mehrere Möglichkeiten, die ein Powershellprogrammierer nutzen kann, um spezielle Dateien und Verzeichnisse zu ermitteln. 3 davon sind:

1.1.1.1 automatic Variables

Technet: about_Automatic_Variables

Neben vielen anderen Variablen enthält die Liste der "automatic variables" auch 6 Variablen, die recht häufig vorkommende Pfade darstellen.

 

Beispiel 1a: Liste der Pfadvariablen unter den "automatic variables"

Lest euch die Beschreibungen einfach mal durch. Einige Variablen davon sind euch bestimmt schon begegnet. Beispielwerte von einem Windows7 System findet ihr im nach folgenden Beispiel 1b.

Get-Help about_automatic_variables
#Ausgabe gekürzt

    $Home
       Enthält den vollständigen Pfad zum Stammverzeichnis des
       Benutzers. Diese Variable ist die Entsprechung der
       %homedrive%%homepath%-Umgebungsvariablen, normalerweise
       "C:\Dokumente und Einstellungen\<Benutzer>".

    ....


    $MyInvocation
       Enthält ein Objekt mit Informationen zum aktuellen Befehl,
       z. B. ein Skript, eine Funktion oder einen Skriptblock.
       Mithilfe der Informationen im Objekt, z. B. mit dem Pfad und dem
       Dateinamen des Skripts ($myinvocation.mycommand.path) oder mit dem
       Namen einer Funktion ($myinvocation.mycommand.name), können Sie den
       aktuellen Befehl bestimmen. Dies ist besonders hilfreich beim Suchen
       des Namens des derzeit ausgeführten Skripts.

    ....

    $Profile
       Enthält den vollständigen Pfad des Windows PowerShell-Profils
       für den aktuellen Benutzer sowie der aktuellen Hostanwendung.
       Mithilfe dieser Variablen können Sie das Profil in Befehlen
       darstellen. Mit der Variablen können Sie beispielsweise in
       einem Befehl bestimmen, ob ein Profil erstellt wurde:

           test-path $profile

       Mit der Variablen können Sie in einem Befehl auch ein Profil
       erstellen:

           new-item -type file -path $pshome -force

       Darüber hinaus können Sie sie in einem Befehl verwenden, um
       das Profil in Editor zu öffnen:

           notepad $profile

    ....

    $PsHome
       Enthält dem vollständigen Pfad des Installationsverzeichnisses für
       Windows PowerShell, normalerweise "%windir%\System32\WindowsPowerShell
       \v1.0". Sie können diese Variable in den Pfaden für Windows
       PowerShell-Dateien angeben. Mit dem folgenden Befehl werden z. B. die
       konzeptuellen Hilfethemen nach dem Wort "Variable" durchsucht:

            select-string -pattern variable -path $pshome\*.txt

    ....  

    $PSScriptRoot
       Enthält das Verzeichnis, aus dem das Skriptmodul ausgeführt wird.
       Diese Variable ermöglicht Skripts den Zugriff auf andere
       Ressourcen über den Modulpfad.
       In Windows PowerShell 2.0, this variable is valid only in script modules
       (.psm1). Beginning in Windows PowerShell 3.0, it is valid in all scripts.
       ....

    $Pwd
       Enthält ein Pfadobjekt, das den vollständigen Pfad des
       aktuellen Verzeichnisses darstellt.

    ....

SEE ALSO
    about_Hash_Tables
    about_Preference_Variables
    about_Variables

Technet: about_Automatic_Variables

$Pwd ($Pwd.Path) und Get-Location ( (Get-Location).Path ) sind identisch

$PSScriptRoot ist ab Powershell 3.0 durchgängig einsetzbar

 
Beispiel 1b: Werte der Pfadvariablen

Speichert bitte das Skript vor der Ausführung ab!

Set-StrictMode -Version "2.0"
Clear-Host
 

"`$Home: {0}" -f $home
"`Skriptpfad: {0}" -f $($MyInvocation.InvocationName)
"`$Profile: {0}" -f $Profile
"`$PsHome: {0}" -f $PsHome
"`$PSScriptRoot: {0}" -f $PSScriptRoot
"`$Pwd: {0}" -f $Pwd
#mögliche Ausgabe

$Home: C:\Users\karl_napf
Skriptpfad: C:\Powershell\MyScripts\test.ps1
$Profile: C:\Users\karl_napf\Documents\WindowsPowerShell\Microsoft.PowerShellISE_profile.ps1
$PsHome: C:\Windows\System32\WindowsPowerShell\v1.0
$PSScriptRoot: C:\Powershell\MyScripts
$Pwd: C:\Powershell\MyScripts

Im Gegensatz zu den anderen 4 Variablen, sind die Variablen $Pwd und $MyInvocation keine Strings der Klasse [System.String], sondern $Pwd ist ein Objekt der Klasse [System.Management.Automation.PathInfo] und $MyInvocation ist ein Object der Klasse [System.Management.Automation.InvocationInfo].

$Pwd  + "\test" führt daher zu einem Fehler, wohingegen $PsHome + "\test" problemlos funktioniert. Der Path als String ist in der Eigenschaft $Pwd.Path gespeichert: $Pwd.Path  + "\test"

Auf $MyInvocation gehe ich im Unterkapitel 1.1.2 " das aktuelle Arbeitsverzeichnis" im Beispiel 2 näher ein.

 

Beispiel 2: Provider des aktuellen Pfades bestimmen ($pwd)

Set-StrictMode -Version "2.0"
Clear-Host

 
Set-Location HKLM:\SYSTEM\CurrentControlSet
$Pwd
$Pwd.Provider.Name

"`nProviderwechsel`n"

Set-Location $env:windir
$Pwd
$Pwd.Provider.NamePath       
#Ausgabe

----
HKLM:SYSTEM\CurrentControlSet
Registry

Providerwechsel

C:\Windows
FileSystem

Durch die Provider wird das Navigieren im lokalen Filesystem, in der lokalen Registry oder im Zertifikatsstore einheitlich und transparent. Für Remotezugriffe sind die Provider allerdings oft nicht geeignet. Hier sind .Net- oder WMI-Klassen erforderlich.

Mehr Infos zu den genannten und weiteren Powershellprovidern findet ihr in der Technet unter: Windows PowerShell – Provider-Hilfethemen

 

1.1.1.2 Umgebungsvariablen - Environment Variables

Technet: about_Environment_Variables

Umgebungsvariablen stellen eine Reihe von Informationen des Betriebssystems und des Userprofiles direkt als Variable zur Verfügung. Darunter sind -deswegen behandle ich das Thema hier- auch etliche Pfade. 
 
Beispiel 1: Anzeige der Umgebungsvariablen

Set-StrictMode -Version "2.0"
Clear-Host


Set-Location env:
Get-ChildItem 

#[System.Environment]::GetEnvironmentVariables()
#mögliche Ausgabe

Name                           Value                                                    
----                           -----                                                    
ALLUSERSPROFILE                C:\ProgramData                                           
APPDATA                        C:\Users\karl_napf\AppData\Roaming                       
CLIENTNAME                     V100WPWMK1II328                                          
CommonProgramFiles             C:\Program Files\Common Files                            
CommonProgramFiles(x86)        C:\Program Files (x86)\Common Files                      
CommonProgramW6432             C:\Program Files\Common Files                            
COMPUTERNAME                   DOM1CLI01                                                
ComSpec                        C:\Windows\system32\cmd.exe                              
DEFLOGDIR                      C:\ProgramData\McAfee\DesktopProtection                  
FP_NO_HOST_CHECK               NO                                                       
HOMEDRIVE                      C:                                                       
HOMEPATH                       \Users\karl_napf                                         
LOCALAPPDATA                   C:\Users\karl_napf\AppData\Local                         
LOGONSERVER                    \\DOM1DC01                                               
NUMBER_OF_PROCESSORS           1                                                        
OS                             Windows_NT                                               
Path                           C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbe...
PATHEXT                        .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC    
PROCESSOR_ARCHITECTURE         AMD64                                                    
PROCESSOR_IDENTIFIER           Intel64 Family 6 Model 23 Stepping 6, GenuineIntel       
PROCESSOR_LEVEL                6                                                        
PROCESSOR_REVISION             1706                                                     
ProgramData                    C:\ProgramData                                           
ProgramFiles                   C:\Program Files                                         
ProgramFiles(x86)              C:\Program Files (x86)                                   
ProgramW6432                   C:\Program Files                                         
PSModulePath                   C:\Users\karl_napf\Documents\WindowsPowerShell\Modules...
PUBLIC                         C:\Users\Public                                          
SESSIONNAME                    RDP-Tcp#0                                                
SystemDrive                    C:                                                       
SystemRoot                     C:\Windows                                               
TEMP                           C:\Users\KARL_N~1\AppData\Local\Temp                     
TMP                            C:\Users\KARL_N~1\AppData\Local\Temp                     
USERDNSDOMAIN                  DOM1.INTERN                                              
USERDOMAIN                     DOM1                                                     
USERNAME                       Karl_napf                                                
USERPROFILE                    C:\Users\karl_napf                                       
VS90COMNTOOLS                  c:\Program Files (x86)\Microsoft Visual Studio 9.0\Com...
VSEDEFLOGDIR                   C:\ProgramData\McAfee\DesktopProtection                  
windir                         C:\Windows                                               
windows_tracing_flags          3                                                        
windows_tracing_logfile        C:\BVTBin\Tests\installpackage\csilogfile.log  
  • Weitere interessante Informationen zum Betriebssystem liefert die WMI-Klasse Win32_Computersystem, wobei dort keine Pfade enthalten sind. 
  • Mit jeder Windowsversion kommen neue Umgebungsvariablen dazu. Dies ist eine Liste von einem Win7-Domänenclient. Unter XP sind es einige Variablen weniger

Manchen Umgebungsvariablen merkt man gelegentlich ihre Vergangenheit an. So sind der TEMP- und TMP Pfad oder Teile davon auf 8 Stellen gekürzt.

Falls diese Verkürzung stört, muss man auf .Net ausweichen: MSDN - System.IO Path-Klasse

$OutputPath =[IO.Path]::GetTempPath()
$OutputPath
#Ausgabe
C:\Users\karl_napf\AppData\Local\Temp\


Beispiel 2: Erstellen von Umgebungsvariablen

Technet: Creating and Modifying Environment Variables

In Beispiel 1 wurden alle Umgebungsvariablen des Systems angezeigt. Genaugenommen haben wir aber die Variablen aus einem userspezifischen Store und einem maschinenspezifischen Store ausgelesen. Beide Stores befinden sich in lesbarer und beschreibbarer Form in der Registry unter

HKEY_CURRENT_USER\Environment  (userspezifisch)
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment  (maschinenspezifisch)

Einige zusätzliche Variablen wie "Userprofile" werden dynamisch erzeugt. Diese Variablen sind  "prozessspezifisch." Diese prozessspezifischen Umgebungsvariablen, die nicht in der Registry stehen, sind beim nächsten Start der Powershell daher auch wieder verschwunden.

In maschinenspezifische Umgebungsvariablen kann man beispielsweise Werte während der Installation hineinsetzen, wie Standort, Inventarnummer oder auch Werte, die später bei weiteren Softwareninstallationen benötigt werden. Wenn man aus den Umgebungsvariablen den Serverfunktionstyp (Domaincontroller, Exchangeserver) auslesen kann, oder ob der Server physikalisch oder virtuell läuft, erleichtert dies die spätere Administration der Maschine.

(ob eine Maschine virtuell oder physikalisch exisitiert, kann man übrigens einfach per WMI erfahren: siehe: Grundlagen - Automation Interfaces - WMI -> 4.2 Informationen zum Betriebssystem -> Beispiel 2a

Set-StrictMode -Version "2.0"
Clear-Host

#Setzen einer userspezifischen Umgebungsvariable (dauerhaft)
$Name="uservariable_1"
$Wert="uservalue_1"
$Context="user"
[Environment]::SetEnvironmentVariable($Name,$Wert,$Context)
#unter HKEY_CURRENT_USER\Environment

#Setzen einer maschnenpezifischen Umgebungsvariable (dauerhaft)
$Name="machinevariable_1"
$Wert="machine_value_1"
$Context="machine"
[Environment]::SetEnvironmentVariable($Name,$Wert,$Context)
#unter HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment

#Setzen einer prozesspezifischen Umgebungsvariable (flüchtig)
$Name="processvariable_1"
$Wert="process_value_1"
$Context="process"
[Environment]::SetEnvironmentVariable($Name,$Wert,$Context)
#$env:$name = $wert #alternativ

Das Ergebnis ist eine neue permanente, maschinenspezifische Umgebungsvariable:

 

Beispiel 3: Auslesen einer Umgebungsvariablen

Set-StrictMode -Version "2.0"
Clear-Host


$Windir=[System.Environment]::GetEnvironmentVariable("Windir")
#identisch zu 
#$Windir=$Env:Windir  

$Windir
#Ausgabe

C:\Windows

Da "Windir" bereits zum Prozessstart als Maschinenvariable vorhanden war, ist sie automatisch auch eine Prozessvariable. Dadurch kann sie einfach ohne Angabe des Kontextes ausgelesen werden. 

Um die im vorigen Beispiel neu erzeugten User- und Machinenvariablen auszulesen, muß entweder Powershell neu gestartet werden, oder der Kontext mit angegeben werden.

Set-StrictMode -Version "2.0"
Clear-Host 
$Name="machinevariable_1"

$Context="machine"
[Environment]::GetEnvironmentVariable($Name,$Context)
#Ausgabe
machine_value_1

Anmerkung: Variablen mit Prozesskontext sind sofort verfügbar, aber nicht Powershellsession übergreifend, sondern nur in der aktuellen Session. Man kann also nicht zwei parallele Powershellsessions geöffnet haben und Umgebungsvariablen untereinander austauschen.

Beispiel 4: Verändern der Umgebungsvariablen "Path"

Programme oder ausführbare Dateien, die sich unter einem, der in dieser Umgebungsvariable definierten Pfade befinden, können ohne Angabe der Pfade aufgerufen werden. Unter Windows 7 befindet sich die zugehörige GUI hier:

 
Um mit Powershell die Path-Variable zu verändern, könnt ihr dieses Skript benutzen:

Set-StrictMode -Version "2.0"
Clear-Host

Function AddPathVariable{
  Param($AdditionalPath)
  $Scope = "User"

  $Paths = @([Environment]::GetEnvironmentVariable("Path", $Scope) -split ";")
  $Paths += $AdditionalPath
  $NewNamePath = $Paths -join ";"

  [Environment]::SetEnvironmentVariable("Path", $NewNamePath, $Scope) | Out-Null
}#end Function

AddPathVariable "c:\PSScripts"
"NewPathVariable: {0}" -f [Environment]::GetEnvironmentVariable("Path", "User")
#mögliche Ausgabe

NewPathVariable: C:\Program Files; c:\myScripts;c:\PSScripts

 

1.1.1.3 GetFolderpath - Specialfolder (.Net)

Neben den gezeigten Umgebungsvariablen und den "automatic variables" gibt es noch die SpecialFolder, die in der Registry unter HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders aufgelistet sind. Diese Pfade können auch über Group Policies aus dem ActiveDirectory manipuliert werden. siehe Technet: Folder Redirection Overview

 

Beispiel 1: Anzeige der Specialfolder mit Pfad

MSDN: Special Folders sample using PowerShell von Thomas Lee

# Author: Thomas Lee

# Get the list of special folders
$Folders = [System.Enum]::GetValues([System.Environment+SpecialFolder])

# Display these folders
"Folder Name            Path"
"-----------            -----------------------------------------------"
foreach ($Folder in $Folders) {
"{0,-22} {1,-15}"  -f $Folder,[System.Environment]::GetFolderPath($Folder)
}

#mögliche Ausgabe

Folder Name            Path
-----------            -----------------------------------------------
Desktop                C:\Users\karl_napf\Desktop
Programs               C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\Start Menu\Programs
Personal               C:\Users\karl_napf\Documents
Personal               C:\Users\karl_napf\Documents
Favorites              C:\Users\karl_napf\Favorites
Startup                C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
Recent                 C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\Recent
SendTo                 C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\SendTo
StartMenu              C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\Start Menu
MyMusic                C:\Users\karl_napf\Music
DesktopDirectory       C:\Users\karl_napf\Desktop
MyComputer                           
Templates              C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\Templates
ApplicationData        C:\Users\karl_napf\AppData\Roaming
LocalApplicationData   C:\Users\karl_napf\AppData\Local
InternetCache          C:\Users\karl_napf\AppData\Local\Microsoft\Windows\Temporary Internet Files
Cookies                C:\Users\karl_napf\AppData\Roaming\Microsoft\Windows\Cookies
History                C:\Users\karl_napf\AppData\Local\Microsoft\Windows\History
CommonApplicationData  C:\ProgramData
System                 C:\Windows\system32
ProgramFiles           C:\Program Files
MyPictures             C:\Users\karl_napf\Pictures
CommonProgramFiles     C:\Program Files\Common Files

 

Beispiel 2: Zuweisen eines Specialfolder-Pfades zu einer Variablen

$InternetCache=[Environment]::GetFolderPath("InternetCache")
$InternetCache
#Ausgabe

C:\Users\karl_napf\AppData\Local\Microsoft\Windows\Temporary Internet Files

Diese Information könnte man dazu benutzen, regelmäßig den Cache des InternetExplorers zu bereinigen, der seine Dateien hier ablegt.
 

1.1.2 das aktuelle Arbeitsverzeichnis und das eigene Skript

Beispiel 1a: Ändern und Abfragen des aktuellen Powershellverzeichnisses

Set-StrictMode -Version "2.0"
Clear-Host  

"lokales Navigieren:"
Set-Location "c:\temp"
Get-Location #identisch mit $pwd (siehe automatic variables)

"Remote Navigieren:"
Set-Location "\\192.168.178.254\c$\temp"
Set-Location "\\dom1dc01\c$\temp"
Get-Location

#Ausgabe

lokales Navigieren:
Path          
---- 
C:\temp

Remote Navigieren:
Microsoft.PowerShell.Core\FileSystem::\\dom1dc01\c$\temp

 
Anzeige der Aliase von Get-Location und Set-Location 

Set-StrictMode -Version "2.0"
Clear-Host  

"Aliase von Get-Location"
Get-Alias -Definition Get-Location
"`nAliase von Set-Location"
Get-Alias -Definition Set-Location
#Ausgabe

CommandType     Name                                 Definition 
-----------     ----                                 ---------- 
Alias           gl                                   Get-Location 
Alias           pwd                                  Get-Location

Aliase von set-location
Alias           cd                                   Set-Location  
Alias           chdir                                Set-Location 
Alias           sl                                   Set-Location 

siehe: My Powershell -> Aliase -> Überblick ->Beispiel 3

Das Beispiel habe ich eingefügt, weil man für den Pfadwechsel meist kurze Befehle verwenden will.
 

Beispiel 1b: Ändern und Abfragen des aktuellen Verzeichnisses mit .Net-Klassen

Set-StrictMode -Version "2.0"
Clear-Host  

#Setzen des aktuellen Verzeichnisses
[IO.Directory]::SetCurrentDirectory("c:\")

#Abfrage mit der DirectoryInfo-Klasse
$a=New-Object System.Io.DirectoryInfo(".")
"{0} -- {1}" -f 'System.Io.DirectoryInfo(".")', $a.Fullname

#identisch zu
$b=[System.Io.DirectoryInfo]"."
"{0} -- {1}" -f '[System.Io.DirectoryInfo]"."', $b.Fullname

#Abfrage mit der Directory-Klasse
$c=[System.IO.Directory]::GetCurrentDirectory()
"{0} -- {1}" -f '[System.IO.Directory]::GetCurrentDirectory()', $c


#Setzen des Arbeitsverzeichnisses mit cmdlet
Set-Location "c:\"


#Abfrage mit PS-cmdlet
$d=Get-Location
"{0} -- {1}" -f 'Get-Location', $d
#Ausgabe

System.Io.DirectoryInfo(".") -- c:\temp
[System.Io.DirectoryInfo]"." -- c:\temp
[System.IO.Directory]::GetCurrentDirectory() -- c:\temp
Get-Location -- C:\

Man erkennt, daß die Powershell und die Klassen unter System.IO unterschiedliche Arbeitsverzeichnisse haben.
 

Beispiel 1c: Ändern des Arbeitsverzeichnisses und Zurücksetzen

Zu Batchzeiten erledigten diese Aufgabe die zwei Befehle pushd und popd. In der Powershell stehen beide als Aliase der cmdlets push-location und pop-location zur Verfügung. Man kann mit push-location mehrere Pfade nacheinander auf einen Standard oder einen selbstdefinierten Stack legen und mittels pop-location in umgekehrter Reihenfolge wieder aufnehmen.

Ich persönlich finde die Speicherung von Pfaden in Variablen mit aussagekräftigen Namen übersichtlicher. Aber es mag sein, daß für bestimmte Aufgaben die Flexibilität der Stacks mehr für popd/ pushd spricht.
Das Beispiel zeigt einmal den Weg über eine Variable und einmal den Weg über den Stack mittels der cmdlets push-location und pop-location. Das Ergebnis ist identisch.

Set-StrictMode -Version "2.0"
Clear-Host  


"Arbeitsverzeichnis in einer Variable speichern `n"
$Ursprungspfad=Get-Location
"Ursprungspfad: $Ursprungspfad"

Set-Location c:\

"geänderter Pfad:  $(Get-Location)"
Set-Location $Ursprungspfad

"zurückgeänderter Ursprungspfad $(Get-Location)"

"Arbeitsverzeichnis im Stack speichern `n"

"Ursprungspfad=$(Get-Location)"
Pushd   #Alias von Push-Location

Set-Location c:\

"geänderter Pfad:  $(Get-Location)"
Popd   #Alias von Pop-Location

"zurückgeänderter Ursprungspfad $(Get-Location)"
#Ausgabe

Arbeitsverzeichnis in einer Variable speichern

Ursprungspfad: C:\temp
geänderter Pfad:  C:\
zurückgeänderter Ursprungspfad C:\temp

Arbeitsverzeichnis im Stack speichern

Ursprungspfad=C:\temp
geänderter Pfad:  C:\
zurückgeänderter Ursprungspfad C:\temp

Beispiel 2a: Ermitteln des eigenen Skriptnamens mit Pfad
Mit der "automatic variable" $MyInvocation, die ein Objekt der MyInvocation-Klasse darstellt, kommt man über die Eigenschaft InvocationName an den vollqualifizierten Namen des aufrufenden Skripts und möglicherweise weitere interessante Informationen, siehe MSDN: InvocationInfo Properties. Leider funktioniert dieser Aufruf aber nur im Hauptprogramm, nicht innerhalb von Functions

Clear-Host
Set-StrictMode -Version "2.0"

$ThisScriptFullPath = $($MyInvocation.InvocationName)
"Pfad dieses Skripts: $ThisScriptFullPath"

$ThisScriptPath = Split-Path $($MyInvocation.InvocationName) -Parent
"Verzeichnis des Skripts:$ThisScriptPath"
#mögliche Ausgabe

Pfad dieses Skripts: H:\temp\25.ps1
Verzeichnis des Skripts:H:\temp

Das Skript muss gespeichert sein und der Aufruf funktioniert nicht innerhalb einer Funktion
 

Beispiel 2b: Ermitteln des eigenen Skriptnamens mit Pfad - auch innerhalb von Funktionen

Clear-Host
Set-StrictMode -Version "2.0"

Function Get-Scriptpath{
  #maximale Verschachtelungstiefe der Funktionen festlegen
  $MaxDepth = 5
 
  For ($i=$MaxDepth;$i -ge 0;$i--){
    Try{
      $Invocation = (Get-Variable MyInvocation -Scope $i).Value
      $ThisScriptPath = Split-Path $Invocation.MyCommand.Path
      $ThisScriptFullPath = $($MyInvocation.InvocationName)
      break
     }Catch{
     If($i -eq 0){
      Write-Host "Please save your script"
     }#if
    }#Try/Catch
  }#For
  Return $ThisScriptPath, $ThisScriptFullPath
}

$ThisScriptPath = (Get-Scriptpath)[0]
$ThisScriptFullPath = (Get-Scriptpath)[1]
Write-Host "ThisScriptPath: $ThisScriptPath"
Write-Host "ThisScriptFullPath: $ThisScriptFullPath"
Out-File -FilePath "$ThisScriptPath\mylog.txt" -InputObject "Hello Karl" -Append
#mögliche Ausgabe

ThisScriptPath: H:\Powershell\ldapQuery
ThisScriptFullPath: Get-Scriptpath

Entscheidend ist in dieser Funktion der Positionsparameter -Scope

Technet: Get-Variable

 

Beispiel 2c: Ermitteln des eigenen Skriptnamens mit Pfad - als Funktion

Function Main{
   Get-ScriptParentPath
}
 
Function Get-ScriptParentPath{
  $Invocation = (Get-Variable MyInvocation -Scope 2).Value
  $ThisScriptPath = Split-Path $Invocation.MyCommand.Path
  $ThisScriptFullPath = $($MyInvocation.InvocationName)
  Return $ThisScriptPath
}  
 
Main


 

Beispiel 2d: Ermitteln des SkriptPfads - auch innerhalb von Funktionen

Ab der Powershell 3.0 gibt es automatic Variable $PSScriptRoot, mit der sich bequem der Pfad des Skripts ermitteln lässt

$ScriptPath = $PSScriptRoot
$ScriptPath
#mögliche Ausgabe

H:\temp

Technet: about_Scripts

$PSScriptRoot
       Contains the directory from which a script is being run. In
       Windows PowerShell 2.0, this variable is valid only in script modules
       (.psm1). Beginning in Windows PowerShell 3.0, it is valid in all scripts.


1.1.3 Skriptausgaben in Dateien unter bestimmten Pfaden ausgeben

Wollt ihr die Ergebnisse eines Skripts in einer Datei  festhalten, so möchtet ihr euch wahrscheinlich um den Ausgabepfad nicht besonders viele Gedanken machen. Mit den folgenden Funktionen wird immer eine neue Datei erstellt, die durch ihren Datumsstempel eindeutig ist.

Beispiel 1: Ausgabepfad neben das Skript setzen (Powershell V3.0)
Mit dieser Funktion könnt ihr euer Ergebnis in eine Datei in dasselbe Verzeichnis schreiben, in der auch euer Skript liegt. Die Datei bekommt noch einen sekundengenauen Zeitstempel verpasst

Clear-Host
Set-StrictMode -Version "2.0"

$FileName = "MyData_"
$ScriptPath = $PSScriptRoot
$TimeStamp = (Get-Date).ToString("yyyymmDD_hhMMss")
$FullOutPath = "$ScriptPath\$Filename$TimeStamp.csv"

$FullOutPath
#mögliche Ausgabe

Y:\PS\MyData_201411DD_080659.csv

Beispiel 1: Ausgabepfad neben das Skript setzen (Powershell V2.0)

Mit dieser Funktion könnt ihr euer Ergebnis in eine Datei in dasselbe Verzeichnis schreiben, in der auch euer Skript liegt. Die Datei bekommt noch einen sekundengenauen Zeitstempel verpasst

Clear-Host
Set-StrictMode -Version "2.0"

Function Main{
   $OutPath = Get-ScriptFolderAsOutput
   $OutPath
}

Function Get-ScriptFolderAsOutput{
     $NestingLevel = 2
     $TimeStamp = (Get-Date).ToString("yyyymmDD_hhMMss")
     $FileName = "MyData_"

     Try{
      $Invocation = (Get-Variable MyInvocation -Scope $NestingLevel).Value
      $ThisScriptPath = Split-Path $Invocation.MyCommand.Path
     }Catch{
       Write-Host "Please save your script"
     }#Try/Catch

    $OutPath = "$ThisScriptPath\$Filename$TimeStamp.csv"
    Return $Outpath  
}

Main
#mögliche Ausgabe

Y:\PS\MyData_201442DD_070658.csv


Beispiel 2: Ausgabepfad in ein festes Verzeichnis setzen (Powershell V2.0)

Mit dieser Funktion könnt ihr euer Ergebnis in eine Datei in ein bestimmtest Verzeichnis (hier: c:\temp) schreiben, in der auch euer Skript liegt. Die Datei bekommt noch einen sekundengenauen Zeitstempel verpasst

Clear-Host
Set-StrictMode -Version "2.0"

Function Main{
   $OutPath = "C:\temp"
   $FileName = "MyData_"
   $FullOutPath = Get-FolderAsOutput $OutPath $FileName
   $FullOutPath
}

Function Get-FolderAsOutput{
     Param($OutPath,$FileName)
     
     $TimeStamp = (Get-Date).ToString("yyyymmDD_hhMMss")
     $FullOutPath = "$OutPath\$Filename$TimeStamp.csv"
     Return $FullOutpath  
}

Main
#mögliche Ausgabe

C:\PS\MyData_201454DD_070639.csv

 

1.2 Powershellcmdlets zur Pfadbearbeitung

Steht der gewünschte Pfad in keiner Systemvariablen bereit, gibt eine Handvoll cmdlets, die dann auf ihren Einsatz warten.

Beispiel 1: Anzeige der cmdlets zur Pfadbearbeitung

Get-Help *path* -auto
#Ausgabe

Name                     Category  Synopsis
----                     --------  --------
Join-Path                Cmdlet    Kombiniert einen Pfad und einen untergeordneten Pfad zu einem Pfad....
Convert-Path             Cmdlet    Konvertiert einen Pfad aus einem Windows PowerShell-Pfad in ...
Split-Path               Cmdlet    Gibt den angegebenen Teil eines Pfads zurück.
Test-Path                Cmdlet    Bestimmt, ob alle Elemente eines Pfads vorhanden sind.
Resolve-Path             Cmdlet    Löst die Platzhalterzeichen in einem Pfad auf und zeigt den Inhalt...
about_Path_Syntax        HelpFile  Beschreibt die Formate für vollständige und relative Pfadnamen in

Die meist verwendeten cmdlets sind zumindest bei mir "Split-Path". "Join-Path" und "Test-Path".


Beispiel 2: zentrales Logverzeichnis oberhalb des temp-Verzeichnisses erstellen, in dem jedes Skript ein spezifisches Logfile ablegt

Mit der Funktion "CentralLog" wird an zentraler Stelle -sofern noch nicht vorhanden- ein LogContainer erstellt. Dort erstellt ein PS-Skript, das diese Funktion aufruft, eine Logdatei aus dem Namen des aufrufenden Skriptes, sofern die Logdatei noch nicht existiert.
In diese Logdatei wird das Datum des Skriptaufrufs, sowie eine Message geschrieben und angehängt. Damit kann man sich bei Bedarf einen leichten Überblick verschaffen, wann welche Skripte ausgeführt wurden.

Es gibt anderere Szenarien, in denen es sinnvoller ist, eine bereits vorhandene Logdatei zu überschreiben, oder für jeden Skriptaufruf eine individuelle neue Logdatei zu erstellen, deren Name sich beispielsweise aus dem Scriptnamen und dem aktuellen Datum mit Uhrzeit zusammensetzt.

Hier gehts mir aber um ein sinnvolles Beispiel für den Einsatz einiger "path-cmdlets", wie split-path, join-path und test-path

Function CentralLog{
    param($funLogname,$funMessage)
    $LogContainer="PSLogs"   

    #Schritt 1
    $funTempPath =[io.path]::GetTempPath()
    $funParentTempPath=split-path $funTempPath -parent
    $funPSLogPath=join-path -path $funParentTempPath -ChildPath $LogContainer
    
    #Schritt 2
    $a=Test-Path -path $funPSLogPath
    if ($a -eq $false){
        New-Item -path $funPSLogPath -type directory
        "Das Verzeichnis $funPSLogPath wurde angelegt"
        }else{
        "Verzeichnis $funPSLogPath existiert schon"
        }

    #Schritt 3  
    $funoutpath=$funPSLogpath+"\"+$funLogname
    $funDateTime=get-date -f "dd-MM-yyyy HH:mm:ss"
    out-file -filepath $funOutPath -inputobject ($funDatetime+": "+$funMessage) -append
}

#Schritt 0
$skriptname=$myinvocation.mycommand.name
$Logname = ($skriptname -split "\.")[0]
$Logname=$Logname+".log"
$Message="Skript erfolgreich aufgerufen"
CentralLog $logname $Message

Erklärung:

Schritt 0:
$skriptname=$myinvocation.mycommand.name
$myinvocation ist eine der "automatic variables", die ich in Kapitel 1.1.1.1 behandelt habe. Läge der Aufruf innerhalb der function und nicht im Skript selbst, so würde der Funktionsname zurückgeliefert

$Logname = ($skriptname -split "\.")[0]
Abtrennen der Dateierweiterung .ps1 vom übrigen Skriptnamen

CentralLog $logname $Message
Funktionsaufruf: die Parameter sind durch Leerzeichen getrennt

Schritt 1:
$funTempPath =[io.path]::GetTempPath()
Einer der festgelegten Pfade die ich im Kapitel 1.1.1.2 Umgebungsvariablen vorgestellt habe. Beispielsweise unter WinXP: "C:\Dokumente und Einstellungen\KarlNapf\Lokale Einstellungen\Temp"

$funParentTempPath=split-path $funTempPath -parent
$funPSLogPath=join-path -path $funParentTempPath -ChildPath $LogContainer

Schritt 2:
Prüfen ob das Zielverzeichnis vorhanden ist. Falls nicht, wird es angelegt

Schritt 3:
Datum und Message in die Textdatei schreiben

Anmerkung: Natürlich kann man sich diese Funktion in jedes seiner Skripte hineinkopieren und dann verwenden. Powershell bietet aber auch die Möglichkeit, diese Funktion in einer einzigen Skriptdatei (Bibliothek) zentral abzulegen und diese als Include-File zu importieren. Das kann die Wartung dieser Funktion deutlich vereinfachen.
My Powershell -> 3.3.4 Include File (Dot Sourcing)

1.3 Pfade mit Sonderzeichen

Die Navigation im Filesystem erfolgt über den Fileprovider meist ohne große Anstrengungen. Ein bischen knifflig kann es bei der Verwendung von Sonderzeichen im Ordner- oder Dateinamen werden. Entweder hat man Glück und die Powershellentwickler haben das notwendige cmdlet mit dem Parameter " "-literalpath" ausgestattet, der alle Zeichen und Sonderzeichen so nimmt wie sie sind. Falls das cmdlet diesen Parameter nicht bereitstellt, muß man entweder den Weg über eine .Net Klasse gehen oder mit dem cmdlet new-PSDrive ein neues PSDrive anlegen. Die folgenden Beispiele zeigen die beiden Wege

Beispiel 1: Navigieren zu einem Pfad mit dem Parameter "-Literalpath"
Das cmdlet set-location besitzt den Positionsparameter -literalpath. So ist man in der angenehmen Situation einfach diesen Parameter benutzen zu können und Sonderzeichen wie "[]" werden ohne Schwierigkeiten akzeptiert. 

Set-StrictMode -Version "2.0"
Clear-Host  

#Set-Location c:\temp\test[1] #würde einen Fehler produzieren
Set-Location -literalpath c:\temp\test[1]

Get-Location
#Ausgabe

c:\temp\test[1]

Der Wert des LiteralPath-Parameters wird genau so verwendet, wie er eingegeben wurde. Es werden keine Zeichen als Platzhalter interpretiert. Wenn der Pfad Escapezeichen enthält, müssen Sie ihn in einfache Anführungszeichen einschließen. Einfache Anführungszeichen veranlassen Windows PowerShell, Zeichen nicht als Escapesequenzen zu interpretieren. (aus der Onlinehilfe zu Set-Location)
 
Beispiel 2a: Workaround mit .Net für das cmdlet "Get-Acl"

Set-StrictMode -Version "2.0"
Clear-Host 

 
#Ausgabe von Accesslisten
#Get-Acl c:\temp\test[1] | select * # liefert nichts zurück
([Io.DirectoryInfo]"C:\Temp\Test[1]").GetAccessControl() | select * 
#Ausgabe gekürzt

AccessToString          : VORDEFINIERT\Administratoren Allow  FullControl
                          VORDEFINIERT\Administratoren Allow  268435456
                         ....


Beispiel 2b: Workaround mit dem cmdlet New-PSdrive für das cmdlet "get-acl", das kein -Literalpath enthält

Remove-PsDrive -name myTest -EA 0 #Falls myTest schon vorhanden ist
New-PsDrive -name myTest -PsProvider FileSystem -root "c:\temp\test[1]" 

(Get-Acl myTest:).access
#Ausgabe gekürzt

AccessToString          : VORDEFINIERT\Administratoren Allow  FullControl
                          VORDEFINIERT\Administratoren Allow  268435456....

Für das cmdlet Get-Acl steht kein Parameter "-LiteralPath" zur Verfügung. Daher muß man entweder den kleinen Umweg über eine geeignete .Net Klasse gehen (Fündig wird man für Fileoperationen meist in den Klassen des System.IO - Namespaces. siehe MSDN: System.IO Namespace) oder man erstellt sich mit dem cmdlet New-Psdrive ein PS-Laufwerk, auf das man das cmdlet Get-Acl anwendet. 

Mit deutschen Sonderzeichen wie "äöüß" kommt Powershell übrigens ohne zu Zucken und ohne -literalpath zurecht. 

1.4 Interaktive Pfadeingabe

Manchmal soll der Anwender eines Powershellskriptes während der Laufzeit des Skripts einen Pfad eingeben. Dazu gibt es die beiden Möglichkeit über die Eingabeaufforderung oder über Dialagfelder aus Windows.Forms.

1.4.1 Pfadeingaben über die Eingabeaufforderung

Technet: Verwenden des Cmdlet "Read-Host"

Beispiel 1: Pfadeingabe durch Read-Host mit Überprüfung

Set-StrictMode -Version "2.0"

do{
 $Eingabe = Read-Host -prompt "bitte den gewünschten Pfad eingeben"
 if(Test-Path $Eingabe){
  "{0} ist ein gültiger Pfad `n" -f $Eingabe
  break
 }else{
  "{0} ist ein ungültiger Pfad, bitte korrigieren `n" -f $Eingabe
  }
 }While(0 -lt 1)
  
 $Eingabe
#mögliche Ausgabe

bitte den gewünschten Pfad eingeben: c:\tem
c:\tem ist ein ungültiger Pfad, bitte korrigieren

bitte den gewünschten Pfad eingeben: c:\temp
c:\temp ist ein gültiger Pfad

c:\temp

 

1.4.2 Pfadeingabe mit Dialogfeldern

Während der Laufzeit eines Skriptes Pfade in die Eingabeaufforderung einzutippen, ist relativ fehleranfällig.
Sicherer ist es, Pfade aus einer GUI auszuwählen. Dazu sind die beiden Klassen OpenFileDialog und SaveFileDialog aus dem Microsoft.Win32-Namespace hilfreich.

MSDN: Microsoft.Win32-Namespace

Beispiel 1: Auswählen eines Dateipfades mit der Klasse OpenfileDialog

Set-StrictMode -Version "2.0"

function GetFilePath
{
    param($InitialDirectory="C:\temp",$Filter="Text (*.txt)|*.txt")
    $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $OpenFileDialog.InitialDirectory = $InitialDirectory
    $OpenFileDialog.Filter = $Filter
    
    $ShowDialog = $OpenFileDialog.ShowDialog()
    
    If($Showdialog)
    {
        Return $OpenFileDialog.FileName
    }
    Else
    {
        Write "Abbruch durch Anwender"
    }
} #Ende der Funktion

$InitialDirectory="C:\"
$Filter="Alle Typen (*.*)|*.*"
#$Filter="Office Files (*.docx;*.xlsx;*.pptx)|*.docx;*.xlsx;*.pptx"

#Funktionsaufruf
$FullName=GetFilePath -InitialDirectory "C:\" -Filter $Filter

"Parentpfad: {0}" -f $(Split-Path $FullName -parent)
"Dateiname: {0}" -f $(Split-Path $FullName -leaf)
#mögliche Ausgabe nach Auswahl einer Datei im Dialogfenster

Parentpfad: C:\log
Dateiname: Test.xlsx

Unter WindowsXP versteckt sich dieser Dialog leider im Hintergrund und ist nicht immer gleich zu sehen. Ich schätze, dass dies ein Bug von XP ist. Unter Windows7 erscheint der Dialog jedenfalls immer im Vordergrund.

MSDN: OpenFileDialog-Klasse

Anstelle von OpenfileDialog kann man ebenso die Klasse SaveFiledialog benutzen.

Beispiel 2: Auswählen eines Dateipfades mit der Klasse SaveFileSialog

Set-StrictMode -Version "2.0"

function GetFilePath
{
    param($InitialDirectory="C:\temp",$Filter="Text (*.txt)|*.txt")
    $SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
    $SaveFileDialog.InitialDirectory = $InitialDirectory
    $SaveFileDialog.Filter = $Filter
    
    $ShowDialog = $SaveFileDialog.ShowDialog()
    
    If ($ShowDialog)
    {
        Return $SaveFileDialog.FileName
    }
    Else
    {
        Write "Abbruch durch Anwender"
    }
}

$InitialDirectory="C:\"
$Filter="Alle Typen (*.*)|*.*"
#$Filter="Office Files (*.docx;*.xlsx;*.pptx)|*.docx;*.xlsx;*.pptx"

#Funktionsaufruf
$FullName=GetFilePath -InitialDirectory "C:\" -Filter $Filter

"Parentpfad: {0}" -f $(Split-Path $FullName -parent)
"Dateiname: {0}" -f $(Split-Path $FullName -leaf)
#mögliche Ausgabe nach Auswahl einer Datei im Dialogfenster

Parentpfad: C:\log
Dateiname: Test.xlsx

MSDN: SaveFileDialog-Klasse