Peter’s PowerShell Blog (German only)

Kleine Tipps für Zwischendurch (Teil 16) – der Parameterbindung zuschauen

Februar 1, 2010 · Hinterlasse einen Kommentar

Dieser Tipp wurde in anderen Blogs schon mehrfach vorgestellt, da er aber so nützlich ist (und ich in meinem kommenden PowerShell 2.0-Buch vergessen habe), möchte ich ihn zu mindestens an dieser Stelle noch einmal würdigen. Es geht um das Trace-Command-Cmdlet und sie sich mit seiner Hilfe die Parameterbindung bei der Ausführung eines Befehls per Debug-Meldungen nachvollziehen lässt.

Ein

Trace-Command -psHost -Name ParameterBinding { Get-Childitem C:\Windows *.ps1 –Rec | Remove-Item –WhatIf }

gibt für die einzelnen Schritte während der Parameterbindung Debug-Meldungen aus.

Bezogen auf den obigen Befehl wird deutlich, wie die PowerShell die Parameter Path und Filter von Get-Childitem bindet, und dass das von Get-Childitem über die Pipeline weitergegebene FileInfo-Objekt an den LiteralPath-Parameter (und nicht an den Path-Parameter) von Remove-Item gebunden wird.

Ein (für mich) ungelöstes Rätsel (mit Bitte um Aufklärung)

Muss einen das als Anwender überhaupt interessieren? Das hängt natürlich vom Betrachtungswinkel ab. Wenn man “rätselhaften Phänomenen” auf die Spur kommen möchte ist es oft die einzige Möglichkeit. Ein solches Phänomen ist für mich seit “Jahren” die Behauptung, Parameter, die offiziell nicht für eine Pipeline-Bindung ausgelegt sind, könnten über einen Scriptblock-Parameter trotzdem gebunden werden.

Hier ein Beispiel, das laut Jeffrey Snover funktionieren sollte:

Import-CSV Compliste.csv | Get-WmiObject -Class Win32_BIOS -ComputerName { $_.ComputerName }

Compliste.cvs ist eine kleine CSV-Datei, die eine Spalte “Computername” mit den NETBIOS-Namen von Netzwerkcomputern enthält.

Leider funktioniert es nicht, die Bindung kommt nicht zustande. Wenn ich etwas mehr Zeit und Muße habe, werde ich mir das “Bindungsprotokoll” einmal genauer ansehen (natürlich würde ich mich auch für einen Hinweis freuen – ich hatte das “Problem” schon vor einiger Zeit in der Newsgroup geschildert, aber auch dort keine Antwort erhalten).

Tipp:

Egal, wie eine Bindung zustande kommt, ein Cmdlet benötigt immer seine Pflichtparameter.

Der folgende Befehl listet diese für ein Cmdlet, in diesem Fall Get-WmiObject, auf:

Get-Command Get-Wmiobject | Select -Exp Parametersets | Foreach { “`nParameterset: $($_.Name)”; $_ |  Select -Exp Parameters | Where { $_.IsMandatory } }

Es “stellt sich heraus” (aus der Hilfe erfährt man es natürlich auch, nur nicht so übersichtlich), dass nur der Class-Parameter “mandatory” ist.

→ Einen Kommentar schreibenKategorien: WPSTipps

Die Wahrheit über HAL

Januar 20, 2010 · Hinterlasse einen Kommentar

Nicht ganz ernst gemeint:

function Get-Truth ([char[]]$c) { $c | % { [char](([int](“{0}” -f [int]$c))+1) } }

Get-Truth “HAL” -> “IBM”

Get-Truth “VMS” -> “WNT”

Angeregt wurde ich zu der “Konstruktion” (bei der ich ohne (!) .NET-String-Methoden auskommen wollte) beim Lesen in Bruce Payettes “PowerShell in Action – Teil 2″, das immer noch nicht da ist (lediglich die ersten sieben Kapitel stehen für jene Leser zur Verfügung, die bereit sind, das eBook bereits jetzt zu kaufen;), und das laut Verlag im Juni 2010 kommen soll. Die Version in seinem Buch war natürlich “etwas” kürzer und eleganter.

Update: Nach dem Motto, es gibt immer noch eine kürzere Variante, wenn man nur lange genug herumprobiert:

function Get-Truth { [int[]] [char[]] $Args[0] | foreach { [char]($_+1)} }

Die erste “Fassung” der obigen Funktion war etwas arg umständlich, auch der Nachfolger besticht noch nicht durch Eleganz und Kühnheit. Der “Trick” der aktuellen Fassung besteht darin, die Zeichenkette , die in ihre Buchstaben zerlegt werden soll, zunächst in ein [char[]], also in ein Array von char-Werten (ein einzelnes Zeichen) zu “casten” (sprich zu konvertieren). Der zweite “Trick” besteht darin, dass man aus einem char, der in einen int “gecastet” wird, den ASCII-Code erhält, der nur um eins erhöht werden muss. Durch erneutes Casten in ein char wird daraus wieder das Zeichen, das ausgegeben wird.

→ Einen Kommentar schreibenKategorien: WPSTipps

Tipps für eigene Cmdlets – den aktuellen Pfad feststellen

Januar 20, 2010 · Hinterlasse einen Kommentar

Danach habe ich eine Weile gesucht, dabei ist die Lösung sehr einfach. Wie kann ein Cmdlet bzw. eine Methode eines Cmdlets feststellen, in welchem Verzeichnis sie ausgeführt wird? Ganz einfach dadurch, dass sich die Cmdlet-Klasse von PSCmdlet und nicht von Cmdlet ableitet (die im letzten Blog-Eintrag erwähnten PowerShell-Templates bieten für beide Klassen eine Vorlage an). Dadurch werden über this (bzw. Me) eine Fülle zusätzlicher Informationen zur Verfügung gestellt, z.B. eine SessionState- und eine Host-Property.

Den aktuellen Verzeichnispfad liefert z.B . ein:

_CurPath = Path.Combine(this.SessionState.Drive.Current.Root, this.SessionState.Drive.Current.CurrentLocation);

→ Einen Kommentar schreibenKategorien: WPSTipps

Templates für Cmdlets

Januar 20, 2010 · Hinterlasse einen Kommentar

Wer eigene Cmdlets erstellen möchte benötigt dazu Visual Studio 2005/2008 und ein passendes Template (Projektvorlage). Auch wenn diese optional sind (ein Cmdlet könnte auch als Assemly-Bibliothek angelegt werden), erleichtern sie das Leben ein wenig und sind vor allem für den Einstieg eine Garantie für ein schnelles Erfolgserlebnis. Von Microsoft gibt es leider nichts offiziell, aber natürlich hat sich die Community schon längst auch dieser Herausforderung angenommen. Hier sind zwei Empfehlungen:

http://psvs2008.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=26356

Das Template von Alan Bradley ist recht komfortabel, in dem es z.B. auch eine Vorlage für eine Hilfedatei umfasst, ohne die es praktisch nicht geht.

Alan beschreibt auch sehr schön, wie ein einfaches Cmdlet Schritt für Schritt umgesetzt wird.

http://blogs.msdn.com/daiken/archive/2007/02/07/creating-a-windows-powershell-cmdlet-using-the-visual-studio-windows-powershell-templates.aspx

Dem Template von David Aiken (der für Microsoft arbeitet – ich hatte einmal die Gelegenheit, an einem seiner Workshops teilzunehmen, was sehr interessant war) gebührt die Ehre, das erste Template gewesen zu sein. Außerdem gibt es hier auch eine Projektvorlage für Visual Basic. Dafür fehlt z.B. eine Hilfevorlage.

Beide Downloads werden als Vsi-Datei zur Verfügung gestellt, über die Visual Studio-Templates installiert werden. Nach der Installation sollten die Projektvorlagen in der Kategorie PowerShell angeboten werden (beim Hinzufügen einer Hilfevorlage ist zu beachten, dass diese nur in der allgemeinen Kategorie unter “Eigene Vorlagen” angeboten wird).

Generell sollte es auch mit den Express-Editionen funktionieren, wenngleich ich das nicht getestet habe.

→ Einen Kommentar schreibenKategorien: WPSTipps

PowerShell-Podcasts (gib mir alles)

Januar 19, 2010 · Hinterlasse einen Kommentar

PodCasts sind im Allgemeinen eine feine Sache (z.B. die Angebote des SWR in der Rubrik Wissen und in den anderen Rubrik sind wirklich durchweg hörenswert), insbesondere natürlich, wenn es um die PowerShell geht. Unter der Adresse

http://powerscripting.wordpress.com/

führen Jonathan Walz und Hal Rottenberg ein Blog, auf dem sie regelmäßig PowerShell-Podcasts veröffentlichen. Ein Podcast ist ein Gespräch, das sie mit einer bekannten Persönlichkeit aus der “PowerShell-Szene” führen, und das man sich (mit Musik und ein wenig Werbung unterlegt) als Mp3-Datei herunter laden und z.B. immer dann anhören kann, wenn man mal wieder irgendwo im Stau steht oder eine Wartezeit sinnvoll überbrücken möchte. Insgesamt also eine tolle Sache (der einzige Kritikpunkt, der mir spontan einfällt, dass sie leider den Titel für die einzelnen Podcasts nicht einheitlich gewählt haben und der Name der Mp3-Datei einen Hinweis auf den Inhalt beinhalten sollte – außerdem enthält der aktuelle RSS-Feed einen kleinen Fehler). Die beiden betreiben ihren Blog schon eine Weile, denn in Kürze steht der 100te Podcast auf dem Programm.

Hier ist ein kleines PowerShell-Skript, das alle Podcasts über BITS und die BitsTransfer-Cmdlets herunterlädt. Nun, nicht alle, sondern nur die letzten 10 (die Anzahl kann aber verändert werden), da schnell 500 MByte zusammenkommen (der Download geht aber erstaunlich schnell über die Bühne). Eine Besonderheit der BitsTransfer-Cmdlets ist, das man selber über einen Aufruf von Get-BitsTransfer herausfinden muss, ob die Übertragung abgeschlossen wurde und, solle dies für alle (!) Jobs der Fall sein, die Übertragung über den Aufruf von Complete-BitsTransfer abschließen muss (laut Doku kann man sich dafür bis zu 90 Tage Zeit lassen).

Man kann es sich natürlich noch einfacher machen und das Tool RSSBandit benutzen, das intern BITS für den Download benutzt.

Hier ist das PowerShell-Skript (die folgende Pdf-Datei GetAllPowerShellPodcasts ist keine Pdf-Datei, sondern eine Ps1-Datei mit der Erweiterung .Pdf – man muss nach dem Download lediglich die Erweiterung ändern – anscheinend lassen sich bei WordPress keine beliebigen Dateien hochladen)

# ————————————————————
# Get alle PowerShell-Podcast
# ————————————————————
Import-Module BitsTransfer
$RSSFeedURL =
http://feeds.feedburner.com/Powerscripting?format=xml
$ZielPfad = “C:\PowerShellPodcasts”
if (!(Test-Path -Path $ZielPfad))
{ Md $ZielPfad | Out-Null }
Del $Zielpfad\*.mp3
$RSS = [xml](New-Object -Type
System.Net.Webclient).DownloadString($RssFeedURL)
$Channels = $Rss.rss.channel
$MaxDownloadCount = 10
$DownloadCount = 0
foreach($Item in $Channels.item)
{
$TitelElements = $Item.title -split “-”
if ($TitelElements.Length -gt 0)
{ $Titel = $TitelElements[$TitelElements.Length -1] }
else
{ $Titel = $Item.title }
if ($Titel.StartsWith(” “))
{ $Titel = ($Titel -Split ” “)[1..$Titel.Length] }  # Wow!
try
{
$SourceLink = $Item.origEnclosureLink
$Mp3File = (New-Object -Type System.Uri
$SourceLink).Segments[-1]
$PodcastTitel = “PSPodCast_$($Titel)_.mp3″
$ZielDateipfad = Join-Path -Path $ZielPfad -Child $PodcastTitel
Start-BitsTransfer -Source $SourceLink -Destination $ZielDateipfad -Async -Priority Low # | Out-Null
$DownloadCount++;
if ($DownloadCount-eq $MaxDownloadCount) { break}
}
catch
{ }
}

→ Einen Kommentar schreibenKategorien: WPSTipps

Kleine Tipps für Zwischendurch (Teil 15) – der nicht gerade triviale Umgang mit generischen Collections

Januar 18, 2010 · Hinterlasse einen Kommentar

Die PowerShell besitzt leider ihre Bereiche, die für “Normalsterbliche” nur schwer nachvollziehbar sind (oder sein dürften), da sie einfach gute bis sehr gute Kenntnisse über die .NET-Programmierung voraussetzen. Der positive Aspekt ist, dass auch der “PowerUser” darüber im Allgemeinen nichts wissen muss, aber ein wenig störend ist es schon wenn man hin und wieder mit Details konfrontiert wird, von denen man nicht die leiseste Ahnung hat was sie bedeuten (auf der andern Seite hat das auch seinen Reiz).

Hier ein kleines Beispiel. Auch ein Modul wird bei der PowerShell (natürlich) durch ein Objekt repräsentiert  – in diesem Fall ist es ein Objekt vom Typ PSModuleInfo. Das Objekt besitzt u.a. eine Property ExportedFormatFiles, die für den oder die Pfade von Typeninformationsdateien (Erweiterung .Ps1Xml) steht. Beim BitsTransfer-Modul wird hier z.B. definiert, wie ein BitsJob-Objekt formatiert ausgegeben wird. Ein

Get-Module BitsTransfer).ExportedFormatFiles

gibt den Pfad der Ps1Xml-Datei zurück.

Damit kann es im Allgemeinen auch bewenden lassen, zumal ein angehängtes Get-Member suggeriert, dass die Property einen String liefert. Wer sich jedoch die Mühe macht, die GetType-Methode anzuhängen, um das dahinter stehende Type-Objekt zu erhalten, das die Typ-Information repräsentiert, erhält etwas differenziertes Bild:

PS > (Get-Module BitsTransfer).ExportedFormatFiles.GetType().Name
ReadOnlyCollection`1

Es stellt sich heraus, dass die Property vom Typ ReadOnlyCollection ist, was keine Überraschung ist, denn es kann durchaus mehrere Pfade geben. Aus irgendwelchen Gründen haben sich die Entwickler des Moduls aber nicht für ein Array oder eine einfache Collection, sondern für diesen Spezialtyp entschieden. Doch was hat das `1 im Namen zu bedeuten?

Diese Notation steht für eine sog. generische Collection, bei der alle Elemente von dem Typ sind, der beim Anlegen der Collection angegeben wurde. Die Zahl gibt die Anzahl der Typ-Parameter an, die beim Anlegen der Collection übergeben werden. In diesem Fall ist es ein einzelner Parameter.

Damit wäre auch dieses lebensnotwendige Detail geklärt;)

Möchte man selber eine generische Collection anlegen, wozu es im Allgemeinen keine echte Notwendigkeit geben dürfte, wird es leider etwas komplizierter. Aus Gründen, die vermutlich nur Bruce Payette beantworten könnte, ist ein naheliegendes

$ColNeu = New-Object -Type System.Collections.Generic.List [type]“string”

nicht erlaubt (was auch an der Schreibweise liegen kann) – jedenfalls ist ein nicht gerade ermutigender “Der Typ [System.Collections.Generic.List] kann nicht gefunden werden”-Fehler die Folge, was natürlich nicht stimmen kann, da es den Typ System.Collections.Generic.List gibt (bzw. es stimmt schon, da der Typ einfach falsch geschrieben wurde).

Funktionieren tut es mit der folgenden Varianten, durch die eine generische List-Collection mit String-Elementen angelegt wird:

# Generische Collection anlegen
Clear-Variable Col, ColType
$ColBase = [System.Collections.Generic.List''1]
$ColType = $ColBase.MakeGenericType(“String”)
$Col = New-Object -Type $ColType
$Col.Add(“Pemo”)
$Col.Add(“Bart”)
$Col.Add(“Ernie”)
$Col

Dreh- und Angelpunkt, auf den man aber erst einmal kommen muss, ist das MakeGeneric-Member (der Type-Klasse), das einen generischen Typ zurückgibt, dessen konkreter Typ als Zeichenkette übergeben wird (hier wird normalerweise ein Arrray von Typnamen übergeben, aber es funktioniert auch mit einem einzelnen Namen). MakeGeneric gibt ein Type-Objekt zurück, das dem Type-Parameter des vertrauten New-Object-Cmdlets übergeben wird, um ein Objekt dieses Typs zu erhalten. Dies ist unsere generische Collection (Liste), an die per Add ein paar Werte angehängt werden.

Wer von Generics noch nicht genug hat, kann endlich auch eine ReadOnlyCollection selber anlegen, in dem ihr beim Instanzieren eine vorhandene generische Liste übergeben wird. Leicht gesagt, denn die Umsetzung ist wieder einmal recht trickreich:

$ReadOnlyColBase = [System.Collections.ObjectModel.ReadOnlyCollection''1]
$ReadOnlyColType = $ReadOnlyColBase.MakeGenericType(“String”)
$ReadOnlyCol = New-Object -Type $ReadOnlyColType @(,$Col)
$ReadOnlyCol

Der Trick besteht in diesem Fall darin zu erreichen, dass die Collection $Col beim Instanzieren der Klasse vom Typ ReadOnlyCollection als Ganzes und nicht Element für Element übergeben wird. Das lässt sich offenbar nur erreichen, in dem die Collection in Gestalt eines Array übergeben wird und das unscheinbare Komma (der Kommaoperator ist bereits etwas Gebräuchlicher) dafür sorgt, dass sie als Ganzes übergeben wird.

Muss man das als PowerShell-User im Detail verstanden haben? Ich denke nicht, zumal es dafür einfach zu wenige konkrete Anwendungsfälle gibt (im Zusammenhang mit System Center Operation Manager habe ich ein paar Beispiele gefunden, in denen diese Techniken zum Einsatz kommen, was aber für mich ein Beweis dafür ist, dass die PowerShell-Schnittstelle beim SCS einfach komfortabler sein müsste), wenngleich solche Experimente durchaus ihren Reiz haben. Ich muss allerdings zugeben, dass ich die Lösung ohne die “kollektive Intelligenz” des Internets (sprich mit Hilfe der Suchmaschine) auch nicht so ohne weiteres gefunden hätte. 

→ Einen Kommentar schreibenKategorien: WPSTipps

64 Bit

Januar 16, 2010 · Hinterlasse einen Kommentar

(dies ist mein erster Blog-Eintrag im Jahr 2010).

Das Thema 64 Bit wird 2010 ein wenig wichtiger werden, denn mit SharePoint 2010 wird es nach Exchange Server 2007 und Windows Server 2008 R2 das dritte Server-Produkt als reine 64-Bit-Version geben. Herausfinden, ob ein PowerShell-Skript unter einem 32- oder 64-Bit-Windows läuft oder überhaupt festzustellen, ob man über eine 32- oder 64-Bit-CPU verfügt, wird daher häufiger einmal eine Rolle spielen.

Feststellen, ob man es mit einer 32- oder 64-Bit-CPU zu tun

Dafür gibt es natürlich verschiedene Möglichkeiten. Die einfachste ist sicherlich die Abfrage der Description-Property der Win32_Processor-Klasse. Liefert die folgende Abfrage ein $true, hat man es mit einer 64-Bit-CPU zu tun:

(Gwmi win32_processor | select description) -match “x64″

Alle wichtigen Details über die CPU liefert auch eine Registry-Abfrage:

Get-Itemproperty -Path Registry::HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION\Sy
stem\CentralProcessor -Name ProcessorNameString  |  Select Pr*

Feststellen, ob die PowerShell in einem 64-Bit-Prozess läuft

Diese Abfrage ist erstaunlich einfach, denn man muss lediglich prüfen, ob ein Int-Zeiger 4 Byte oder 8 Byte groß ist:

[System.IntPtr]::Size

→ Einen Kommentar schreibenKategorien: WPSTipps

Kleine Tipps für Zwischendurch (Teil 14) – die ASCII-Codes eines Strings

Dezember 8, 2009 · Hinterlasse einen Kommentar

Wie erhält man eigentlich die ASCII-Codes der einzelnen Zeichen einer Zeichenkette? Zum Beispiel wie folgt:
$Tipp = “Mach mal Urlaub”
$Tipp.ToCharArray() | Foreach-Object { “U+{0:x2}” -f [int]$_ }

Die Typenkonvertierung per [int] ist notwendig. Dass der Zeichencode als Hexadezimalzahl ausgegeben wird natürlich nicht.

→ Einen Kommentar schreibenKategorien: WPSTipps

So ausführlich sollten Fehlermeldungen immer sein…

Dezember 7, 2009 · Hinterlasse einen Kommentar

Nach Ausführen eines Enter-PsSession-Cmdlets mit “CredSSP” für den Authentication-Parameter habe ich folgende Fehlermeldung erhalten;

Enter-PSSession : Beim Verbinden mit dem Remoteserver ist folgender Fehler aufgetreten: Die Anforderung kann vom WinRM-Client nicht verarbeitet werden. Eine Computerrichtlinie ermöglicht nicht die Delegierung der Benutzeranmeldeinformationen an den Zielcomputer. Verwenden Sie “gpedit.msc”, und betrachten Sie die folgende Richtlinie: Computerkonfiguration -> Administrative Vorlagen -> System -> Delegierung von Anmeldeinformationen -> Delegierung von aktuellen Anmeldeinformationen zulassen.  Stellen Sie sicher, dass die Anwendung aktiviert und mit einem für den Zielcomputer geeigneten SPN konfiguriert ist.  Beispiel: Für den Zielcomputernamen “myserver.domain.com” kann der SPN eine der folgenden Bezeichnungen besitzen: WSMAN/myserver.domain.com oder WSMAN/*.domain.com. Weitere Informationen finden Sie im Hilfethema “about_Remote_Troubleshooting”.

Nicht nur, dass die Fehlerursache recht ausführlich beschrieben wird, sie enthält auch eine komplette Schritt für Schritt-Anleitung, mit der sich der Fehler durch Aktivieren einer Gruppenrichtlinie beheben lassen könnte (wenngleich das zumindestens bei mir leider nicht die Fehlerursache war).

Klar, das PowerShell-Team weiß natürlich, dass das die typischen Problemfelder bei PowerShell-Remoting sind. Trotzdem war ich über diese Fehlermeldung positiv überrascht (es fehlt eigentlich nur noch ein “Viel Erfolg!” am Schluss).

→ Einen Kommentar schreibenKategorien: WPSTipps

Auflisten von Netzwerklaufwerken (es lebe die Vielfalt)

Dezember 7, 2009 · Hinterlasse einen Kommentar

Das Auflisten von Netzwerklaufwerken ist im Grunde ganz einfach:

>WMI-Klasse Win32_MappedLogicalDisk
>Registry-Key HKCU\Network
>EnumNetworkDrives-Methode beim WScript.Network-COM-Objekt

Auch ein Get-PsDrive listet die Netzwerklaufwerke mit auf (wobei ich hier auf die Schnelle keine Möglichkeit gefunden habe, ein lokales Laufwerk von einem Netzwerklaufwerk zu unterscheiden).

Seltsamerweise funktioniert auf meinem Vista-Notebook lediglich die Registry-Variante, ein Gwmi Win32_MappedLogicalDisk gibt z.B. nichts zurück (was ein wenig zu wenig ist).

Der folgende Befehl listet z.B. die Namen und Pfade der Netzwerklaufwerke per Registry-Key-Abfrage auf:

Get-ChildItem Hkcu:\\Network | Select-Obect @{Name="Laufwerk";Expression={$_.PSCHildName}}, @{Name="Remotepfad";Expression={$_.GetValue("RemotePath")}}

→ Einen Kommentar schreibenKategorien: WPSTipps