Artikel

Pimp my Shell – auch für Windows

Windows

Wer einmal längere Zeit unter Linux gearbeitet hat, weiß, dass eine vernünftige Shell nicht nur aus einem schwarzen Fenster und einem blinkenden Cursor besteht. Sie ist ein Werkzeug, das sich dem Benutzer anpasst. Mit ein paar gut gewählten Alias-Befehlen wird aus umständlicher Tipparbeit eine ziemlich effiziente Angelegenheit.

Und dann kommt Windows.

Fairerweise muss man sagen: Die PowerShell ist inzwischen ein ausgesprochen mächtiges Werkzeug. Teilweise sogar mächtiger als die gute alte Bash. Aber wer sie so komfortabel bedienen möchte wie eine vernünftig konfigurierte Linux-Shell (hahaha!), muss erst einmal ein wenig nachhelfen.

Denn während ich unter Linux seit Jahrzehnten mit ll, d, .. oder lt durch meine Verzeichnisse flitze, erwartet mich unter Windows zunächst eine eher nüchterne Angelegenheit. Besonders schmerzlich: ls kennt zwar jeder, aber die schöne farbige Ausgabe mit Verzeichnissen zuerst und vernünftigen Zusatzinformationen fehlt zumindest in der klassischen Windows PowerShell.

Das ändern wir jetzt.

Die PowerShell bekommt eine .bashrc

Unter Linux landen solche Anpassungen gewöhnlich in der .bashrc oder .zshrc. Die PowerShell besitzt dafür eine Profildatei, die beim Start geladen wird.

Den Pfad verrät:

$PROFILE

Bei mir ist das:

C:\Users\juergen.schulze\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1

Existiert das Verzeichnis noch nicht, legen wir Verzeichnis und Datei direkt an:

New-Item -ItemType Directory -Path (Split-Path $PROFILE -Parent) -Force | Out-Null
New-Item -ItemType File -Path $PROFILE -Force -ErrorAction SilentlyContinue | Out-Null

Anschließend lässt sich das Profil beispielsweise mit der PowerShell ISE bearbeiten:

powershell_ise.exe $PROFILE

Damit haben wir unsere persönliche Konfigurationsdatei. Alles, was dort steht, steht künftig auch in jeder neuen PowerShell-Sitzung zur Verfügung – sofern die Ausführungsrichtlinie das Laden des Profils erlaubt.

Endlich vernünftige Kurzbefehle

Meine Lieblingsbefehle unter Linux sind denkbar unspektakulär. .. wechselt ein Verzeichnis nach oben, ... gleich zwei. md erstellt Verzeichnisse, und d zeigt den Verzeichnisinhalt ordentlich sortiert an. Keine Raketenwissenschaft, aber wenn man jeden Tag damit arbeitet, spart jeder Tastendruck.

In PowerShell können wir vieles davon mit kleinen Funktionen nachbilden:

# Navigation
function ..  { Set-Location .. }
function ... { Set-Location ../.. }

function Push-Here { Push-Location . }
Set-Alias -Name '+' -Value Push-Here

# Verzeichnisse erstellen
function md {
    param([string]$Path)
    New-Item -ItemType Directory -Path $Path -Force
}

# Nur leere Verzeichnisse entfernen
function rd {
    param([string]$Path)
    [System.IO.Directory]::Delete(
        (Convert-Path -LiteralPath $Path), $false
    )
}

# Befehle finden
function which {
    param([string]$Name)
    Get-Command $Name
}

# Archive entpacken
function untar {
    param([string]$File)
    tar -xvf $File
}

Der Unterschied zwischen Alias und Funktion ist wichtig: Ein PowerShell-Alias ist im Wesentlichen nur ein anderer Name für einen Befehl. Sobald Parameter, Sortierungen oder zusätzliche Logik ins Spiel kommen, brauchen wir eine Funktion.

Das wichtigste Kommando: d

Jetzt kommen wir zum eigentlichen Ärgernis.

Unter Linux verwende ich seit Ewigkeiten:

alias d='ls -la --color=auto --group-directories-first -v'

Das bedeutet: alle Dateien anzeigen, einschließlich versteckter Dateien, mit Zusatzinformationen, farbig, Verzeichnisse zuerst und nach Namen natürlich sortiert.

Genau so soll eine Verzeichnisauflistung aussehen. Nicht als schmucklose Namensliste, sondern als brauchbare Arbeitsansicht.

Die klassische Windows PowerShell 5.1 bekommt das nicht von Haus aus hin. Also bauen wir uns unsere eigene Variante:

function Show-Long {
    $items = Get-ChildItem -Force @args |
        Sort-Object -Property `
            @{ Expression = 'PSIsContainer'; Descending = $true },
            Name

    foreach ($item in $items) {
        $color = if ($item.PSIsContainer) {
            'Blue'
        }
        elseif ($item.Extension -match '^\.(exe|bat|cmd|ps1)$') {
            'Green'
        }
        elseif ($item.Extension -match '^\.(zip|gz|tar|7z|rar)$') {
            'Red'
        }
        else {
            'Gray'
        }

        $size = if ($item.PSIsContainer) {
            ''
        }
        else {
            '{0:N0}' -f $item.Length
        }

        $line = '{0,-6} {1:dd.MM.yyyy HH:mm} {2,14}  {3}' -f `
            $item.Mode,
            $item.LastWriteTime,
            $size,
            $item.Name

        Write-Host $line -ForegroundColor $color
    }
}

Das Ergebnis: Verzeichnisse erscheinen blau, ausführbare Dateien grün, Archive rot. Dazu gibt es Berechtigungsmerkmale, Änderungsdatum und Dateigröße. Verzeichnisse stehen oben.

Die Sortierung ist dabei alphabetisch, nicht exakt die natürliche Versionssortierung von GNU ls -v. Und weil Write-Host die Ausgabe direkt ins Terminal schreibt, eignet sich diese Funktion für die Bildschirmdarstellung, nicht für die Weiterverarbeitung in einer PowerShell-Pipeline. Dafür bleibt Get-ChildItem zuständig.

Jetzt bekommt die Funktion noch ein paar vertraute Namen:

Set-Alias -Name d   -Value Show-Long
Set-Alias -Name dir -Value Show-Long -Option AllScope -Force

Die AllScope-Option bei dir ist übrigens kein Schmuck. PowerShell bringt diesen Alias bereits mit, und beim Überschreiben muss seine bestehende Eigenschaft erhalten bleiben. Andernfalls gibt es eine Fehlermeldung.

Damit reicht künftig ein einziges:

d

Und endlich sieht das Dateisystem wieder halbwegs vernünftig aus.

Noch ein paar alte Bekannte

Meine übrigen Kurzbefehle sind schnell eingerichtet:

function Show-All {
    Get-ChildItem -Force @args
}

function Show-Newest {
    Get-ChildItem -Force @args |
        Sort-Object -Property `
            @{ Expression = 'PSIsContainer'; Descending = $true },
            @{ Expression = 'LastWriteTime'; Descending = $true }
}

Set-Alias -Name l    -Value Show-All
Set-Alias -Name la   -Value Show-All
Set-Alias -Name ll   -Value Show-All
Set-Alias -Name lt   -Value Show-Newest
Set-Alias -Name ls-l -Value Show-Long

lt zeigt die zuletzt geänderten Dateien zuerst, mit Verzeichnissen oben. ll, la und l sind die gewohnten Kurzformen für eine vollständige Verzeichnisauflistung.

Nicht jeder Linux-Befehl braucht allerdings ein Gegenstück. Ein rehash ist in PowerShell normalerweise überflüssig, und bei Dingen wie flush sollte man ohnehin vorsichtig sein. Nicht jeder alte Reflex verdient eine automatische Übersetzung.

Einmal laden, dauerhaft benutzen

Nach dem Speichern des Profils laden wir unsere Änderungen direkt in die laufende Sitzung:

. $PROFILE

Der Punkt vor $PROFILE ist dabei entscheidend. Er sorgt dafür, dass die Datei in der aktuellen Sitzung ausgeführt wird.

Ab jetzt funktionieren unsere neuen Kurzbefehle. Und bei jedem weiteren Start der PowerShell werden sie automatisch geladen.

Windows kann also doch Shell

Nach dieser kleinen Operation fühlt sich die PowerShell schon wesentlich vertrauter an. Natürlich ist sie deshalb noch keine Bash. Sie arbeitet intern mit Objekten statt mit reinen Textströmen, und das ist bei komplexeren Aufgaben sogar ein erheblicher Vorteil.

Aber eine gute Shell sollte nicht verlangen, dass man für eine simple Verzeichnisauflistung eine halbe Kommandozeile eintippt. Sie sollte das tun, was man braucht, und zwar möglichst ohne Umstände.

Linux hat mich in dieser Hinsicht über Jahrzehnte gründlich verdorben. Oder erzogen. Je nachdem, wen man fragt.

Jetzt darf Windows wenigstens ein bisschen mitspielen.

0 Kommentare