PowerShell Profile

Wer sich mit der PowerShell auseinandersetzt, der wird ziemlich schnell auch das Profil seiner PowerShell Umgebung anpassen wollen.
Das Profil ist ähnlich einer .profile für die Bash, eine Konfigurationsdatei, über die nicht nur das Aussehen der Shell angepasst werden kann, sondern in der auch eigene Aliase und Funktionen hinterlegt werden können.
Nur, wie heisst und wo liegt diese Datei?

$profile

...\Eigene Dateien\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

Wir tippen in der Shell einfach $profile ein und voila - der Pfad und Name zu unserer benutzerspezifischen Powershell-Profildatei wird angezeigt. Wenn wir aber das erste Mal im Explorer danach suchen, dann sind in der Regel weder Pfad noch Datei vorhanden.
Die Existenz des Dateipfades kann mit Test-Path $profile überprüft werden. Wird hier False zurückgeliefert, dann muss die Datei erst mit New-Item -Path $Profile -Type File -force erzeugt werden. Zum Bearbeiten öffnen wir die Datei mit notepad $profile.

Eine Datei mit gleichem Namen gibt es auch im Installationsverzeichnis der Powershell unter
%systemroot%\system32\WindowsPowerShell\v1.0. Änderungen in dieser Datei betreffen alle Benutzer. Den Pfad dahin kann man sich mit $pshome anzeigen lassen.

Wie kann ich die Shell anpassen?

$ui = (Get-Host).UI.RawUI
$ui.ForegroundColor = "Black"
$ui.BackgroundColor = "Gray"
$ui.WindowTitle = $env:userdomain + "\" + $env:username + " 's Powershell"

Nicht schwer zu erkennen, dass wir mit diesen vier Zeilen die Farben und den Titel des PowerShell Fensters modifizieren können. Mit $env:username wird auf den PowerShell Provider Environment zugegriffen und man bekommt darüber den angemeldeteten Benutzernamen.
Die Powershell Provider lassen sich mit Get-PSProvider anzeigen.

Im Profil kann mit der folgenden Funktion auch das Aussehen des Prompts modifiziert werden.

function prompt
{
   Write-Host "PS " -nonewline -foregroundcolor Red
   Write-Host $(get-location) "#" -nonewline
   return " "
}

Damit wird die interne Function prompt{} überschrieben und das sieht dann so aus: PS C:\path #

Im Profil können natürlich auch eigene Aliase hinterlegt oder über Get-PSSnapIn Cmdlets von Drittherstellern (z.B. Quest Management Shell for Active Directory) eingebunden werden, um den Funktionsumfang der PowerShell zu erweitern.

Anfangs hat man nicht alle Abkürzungen (Aliase) für die Cmdlets im Kopf, man kann sie aber mit Get-Alias anzeigen lassen. Für mich gut lesbar ist die Liste aber erst, wenn sie nach den Kommandos bzw. Cmdlets sortiert wird. Dazu geben wir ein:
Get-Alias | sort definition | format-table -autosize. Sieht schon besser aus, aber da es für manche Cmdlets mehr als ein Alias gibt, wäre doch ein ListAlias mit folgender Ausgabe nützlich.

la

...
Get-ChildItem     gci, ls, dir
Get-Command     gcm
Get-Content     gc, cat, type
Get-History     ghy, h, history
Get-Item     gi
...

Jedes Kommando bzw. Cmdlet kommt in der Liste nur einmal vor, und falls ein Cmdlet mehrere Aliase hat, dann werden diese hintereinander angezeigt.

Das können wir zum Beispiel mit einer Funktion in unserer Profildatei lösen.

Function myDisp-Alias
{
   $al = @{}
   Get-Alias | foreach {
      if ( $al.ContainsKey($_.definition) ) {
         $temp = $al.Get_Item($_.definition)
         $al.Set_Item($_.definition, $temp + ", " + $_.name)
         } else {
            $al.Add($_.definition , $_.name)
      }
   }
   $al.GetEnumerator() | sort name | more
}
set-alias la myDisp-Alias

Die Function myDisp-Alias benutzt eine Hashtable $al = @{}, die aus Schlüssel=Wert Paaren aufgebaut wird. Wir übergeben das Ergebnis von Get-Alias zeilenweise an eine Foreach-Schleife.
In dieser Schleife bekommen wir je Zeile in der automatischen Variablen $_ ein Objekt mit den Eigenschaften name (Aliasname) und Definition (Name des Cmdlet).
Zuerst wird mit $al.ContainsKey() ausgewertet, ob in der Hashtable ein Eintrag für den Schlüssel $_.definition vorhanden ist. Ist ein Eintrag vorhanden, dann merken wir uns den letzten Wert (Aliasname) zu dem Schlüssel (Cmdlet) mit $al.Get_Item($_.definition) in der Variablen $temp. Dann wird der alte und neue Wert als String addiert und das Schlüssel=Wert Paar neu mit $al.Set_Item($_.definition, $temp + ", " + $_.name) gesetzt.

Ist noch kein Wert=Schlüssel Paar für das aktuelle Cmdlet vorhanden, dann wird mit
$al.Add($_.definition , $_.name) ein neuer Eintrag in die Hashtable hinzugefügt.
Nachdem die Foreach-Schleife durchlaufen ist, werden in der Hashtable $al die Elemente mit der Methode .GetEnumerator() noch nach dem Schlüssel sortiert.

Zuletzt setzen wir mit Set-Alias la myDisp-Alias einen neuen Alias, den wir auf unsere Funktion zeigen lassen.
Jedesmal, wenn wir jetzt la eintippen, wird die Funktion aufgerufen und wir bekommen eine etwas andere Darstellung der vorhandenen Aliase.


Gerade am Anfang kennt man nicht alle korrekten Cmdlet Namen. Da wäre es doch nicht schlecht nach einem String im Namen suchen zu können. Die folgende Funktion macht genau das.

Function findCommandString
{
   param( [string]$s )
   if (!$s) {
      Write-Host "`n FindCommandString examples:`n"
      Write-Host " fcs wmi findet alle Cmdlets, die den String wmi enthalten."
      Write-Host " fcs set findet alle Set-Cmdlets.`n"
      Write-Host " fcs all findet alle Aliase, Cmdlets und Funktionen`n"
      break
   }
   if ($s -eq "all") {
      Write-Host "`nErzeuge alphabetische Liste der Aliase, Cmdlets und Funktionen - `          bitte warten ...`n"
      get-command * | Sort name | Where-Object { ($_.commandtype -eq "alias") -or `
         ($_.commandtype -eq "cmdlet") -or ($_.commandtype -eq "function") } `
         | ft -property commandtype, name, definition -auto | more
      break
   }
   get-command | ? { ($_.verb -match $s) -or ($_.noun -match $s) }
}
set-alias fcs findCommandString

Mit param( [string]$s ) wird der Übergabeparameter an die Funktion definiert. $s ist vom Typ String und mit if (!$s) prüfen wir, ob überhaupt ein Parameter übergeben wird. Wenn nicht, dann erfolgt eine entsprechende Ausgabe auf der Konsole und die Funktion wird mit break abgebrochen.
In der Ausgabe verwenden wir den Escape Character `n der dem vbCRLF in VBSkript entspricht und einen Zeilenumbruch bewirkt.

Wird der Übergabeparameter all gefunden, dann werden alle Aliase, Cmdlets und Funktionen alphabetisch aufgelistet. Von Get-Command werden alle PowerShell Elemente (*) zuerst an eine Pipe zum Sortieren nach Name übergeben und dann wird über Where-Object {} nach dem CommandType Alias, Cmdlet oder Function gefiltert. In der automatischen Variablen $_ steht das jeweilige Objekt aus der Get-Command * Abfrage. Über eine Pipe an ft (Alias für Format-Table) werden die Daten in den Tabellenspalten formatiert und zuletzt wird alles an more zur seitenweise Anzeige übergeben.

Jeder andere Übergabeparameter wird in der nächsten Zeile von Get-Command über eine Pipe an den Alias ? =Where-Object{} übergeben. In dem Filter wird getestet, ob das jeweilige Cmdlet-Objekt ($_) entweder im Verb oder im Adjektiv (Noun) den Suchstring enthält.

Zuletzt lassen wir noch den Alias fcs - gedanklich für FindCommandString - auf die Funktion zeigen und können jetzt z.B. mit fcs item alle Cmdlets finden, die den String 'item' beinhalten, mit fcs get alle Get-Cmdlets oder mit fcs all alle Aliase, Cmdlets und Funktionen auflisten.


Nach dem gleichen Prinzip erstellen wir noch zwei weitere Aliase.

Function myDisp-Env {
   Get-ChildItem ENV: | sort-object -property Name | format-table -autosize
}
set-alias env myDisp-Env

Function myDisp-Vars {
   Get-Variable | sort name | ft -autosize
}
set-alias var myDisp-Vars

Mit den zwei Aliasen env und var können wir die Environement Variablen und die internen PowerShell Variablen anzeigen lassen.

Die einzelnen Codeteile von oben kopieren Sie einfach in Ihre PowerShell Profildatei und haben ein paar zusätzliche Aliase und Funktionen für den Einstieg in die neue Skripting Umgebung.

Nicht vergessen sollten Sie am Anfang der Datei auch eine Zeile mit Set-ExecutionPolicy unrestricted, worüber der Sicherheitskontext eingestellt wird. Mit unrestricted erlauben Sie die Ausführung jeder beliebigen PowerShell Skriptdatei, was natürlich nicht in produktiven Umgebungen eingestellt werden sollte.