Archiv für den Monat: Oktober 2019

PowerShell versus Python – GetMember versus dir()

Bei PowerShell ist Get-Member nicht umsonst eines der wichtigsten Cmdlets. Dank Get-Member erfährt man, welche Members eine Objekt besitzt. Und das ist bei einer Scripting-Umgebung, bei der alles ein Objekt ist, sehr viel wert.

Auch bei Python ist alles ein Objekt (eigentlich andersherum, da Python ca. 15 Jahre älter ist), leider gibt es ein direktes Pendant zu Get-Member (zumal es auch keine Pipeline gibt). Es gibt aber eine Function, die Get-Member relativ nahe kommt. Sie trägt den ungewöhnlichen Namen dir(). Warum ungewöhnlich? Weil dieser Name seit den Zeiten von GW-Basic für einen Befehl/Function steht, die einen Verzeichnisinhalt auflistet. Aber ganz unpassend ist der Name nicht.

Der folgende Befehl geht noch ein wenig weiter, in dem dank List Comprehensions nur Members eines Objekts ausgegeben werden, die nicht mit einem Unterstrich beginnen (damit wird die Ausgabe bereits etwas übersichtlicher).

 [name for name in dir(hits) if not name.startswith("_")]

Die Ausgabe besteht natürlich nicht aus Objekten, sondern lediglich aus Strings. Aber immerhin.

In diesem Vergleich punktet PowerShell. Nicht nur, weil Get-Member mehr Möglichkeiten bietet, sondern weil bei der Rückgabe von dir() etwas Wichtiges fehlt: Die Metadaten über ein Member, also z.B. den Datentyp einer Property.

Tipp des Tages: Verzeichnisse aus der path-Umgebungsvariablen entfernen

Hin und wieder kann es erforderlich sein, einzelne Verzeichnisse aus der path-Umgebungsvariablen zu entfernen. Was sich zunächst schwierig anhören könnte, ist in der Praxis dann doch ein relativ einfacher Befehl.

Der folgende Befehl entfernt alle Verzeichnisse, die ein Python36 enthalten, aus der Path-Umgebungsvariablen.

$env:path = $env:path.split(";").where{$_ -notmatch "Python36"} -join ";"

Die Änderung gilt natürlich nur für die aktuelle PowerShell-Sitzung. Soll sie dauerhaft sein, muss der neue Wert der Umgebungsvariablen über die SetEnvironmentVariable()-Methode der Environment-Klasse für den aktuellen Benutzer oder systemweit gespeichert werden.

Praxistipp: Das Anmeldekonto für einen Systemdienst aktualisieren

Zu den wenigen Einstellungen, die für einen Systemdienst nicht per Set-Service vorgenommen werden können gehört das Ändern des System- oder Benutzerkontos, unter dem der Dienst ausgeführt werden soll. Diese Einstellung muss per WMI, der Klasse Win32_Service und ihrer Change()-Methode vorgenommen werden. Benutzername und Kennwort werden dabei im Klartext übergeben. Eine kleine Herausforderung besteht darin, dass für alle nicht verwendeten Parameter ein $null übergeben werden muss.

Bill Stewart hat ein schönes Skript erstellt ( Set-ServiceCredential.ps1), mit dem das Systemkonto für einen Systemdienst aktualisiert werden kann:

https://gist.github.com/Bill-Stewart/ab3a228903c5d6fb3c12dc1d92d3d1e8

Mir ist das Skript aber etwas zu umfangreich und formal. Die folgende Function erledigt die Einstellung etwas kürzer:

function Set-ServiceAccount
{
  [CmdletBinding()]
  param([String]$Servicename, [String]$Username, [String]$Password)
  Stop-Service -Name $ServiceName
  $service = Get-WmiObject Win32_Service -Filter "Name='$ServiceName'"
  $ret = $service.change($null,$null,$null,$null,$null,$null,$Username,$Password,$null,$null,$null)
  if ($ret.ReturnValue -eq 0)
  {
      Write-Verbose "Kontoinformation wurde erfolgreich aktualisiert."
      Start-Service -Name $ServiceName
  }
  else
  {
      Write-Warning "Die Kontoinformation konnte nicht aktualisiert werden - ReturnValue=$($ret.ReturnValue)"
  }
}

Ging alles gut, ist der ReturnValue = 0. Ansonsten eine Zahl, die einen Fehlercode darstellt. 22 bedeutet z.B., das das Konto nicht stimmt (in dem Skript von Bill Stewart werden alle Fehlercodes abgefragt und durch Fehlermeldungen ersetzt).

Tipp des Tages: Befehlsverlauf (History) durchsuchen mit PsReadline

Der Befehlsverlauf (History) der PowerShell-Konsole besitzt den (kleinen) Nachteil, das er nicht automatisch gespeichert wird und damit beim nächsten Start der Konsole nicht mehr vorhanden ist. Was nicht jeder PowerShell-Anwender wissen dürfte, sobald PSReadline verwendet wird, gibt es einen eigenen Verlauf, der über die Pfeiltasten abgerufen wird (nicht per Get-History). Praktisch ist, dass sich dieser Verlauf auch durchsuchen lässt.

Ex-Scripting Guy Ed Wilson hatte ja bereits schon 2014 (also vor mehr als 5 Jahren) darüber geschrieben:

Lange her, aber immer noch aktuell – der Umgang mit dem Befehlsverlauf der PowerShell-Konsole

Im Grunde läuft es auf den Tastaturshortcut [Strg]+[r] hinaus. Über ihn wird der Befehlsverlauf von PsReadline nach allen Zeilen rückwärts durchsucht, die mit der danach eingegebenen Zeichenfolge beginnen. Für jeden weiteren Treffer drückt man erneut [Strg]+[r]. Für was die Vorwärtssuche per [Strg]+[s] gut sein soll, ist mir dagegen nicht klar. Vielleicht hat jemand eine Erklärung.

Im „neuen“ PSReadline zeigt Get-PSReadlineKeyHandler die Tastaturshortcuts auch sehr übersichtlich an.