none
Fragen zum Pipelining von Parametern RRS feed

  • Frage

  • Hallo,

    ich bin dabei, mir ein PS Modul mit mehreren Funktionen zu erstellen.

    Nachdem ich nun alle "about_Functions..." Dokumente gelesen habe, habe ich meinen bisherigen Quelltext nochmal umgebaut :)

    Nun habe ich folgendes Problem mit der Parameterübergabe/dem Pipelining.

    Ich habe eine Funktion, die zwei Werte, einen String und einen Int zurückgibt.

    In einer weiteren Funktion werden diese Parameter dann übernommen und weiter verarbeitet.

    Sobald ich die zwei Parameter der zweiten Funktion mit ParameterSetName definiere (einmal String, einmal Int) und diese Funktion in der ISE Commandline mit manuell angegebenen Parametern aufrufen möchte, habe ich das Problem, dass sobald ich den ersten Parameter angebe, der zweite Parameter nicht mehr in der Auswahlliste, die ich durch Eingabe des - erhalte, erscheint. Kommentiere ich die ParameterSetName Zeilen aus, kann ich beide Parameter angeben.
    Bei Ausführung dieser Funktion mit definiertem ParameterSet und Angabe beider Parameter erhalte ich die Fehlermeldung "Der Parametersatz kann mit den angegebenen benannten Parametern nicht aufgelöst werden".

    Was mache ich hier falsch?

    Nun mein weiteres Problem. Wenn ich Funktion1 | Funktion2 aufrufe, in meinem Quelltext kein Begin, Process, End nutze und dann $Input, die Pipelinevariablen 1 und 2 ausgebe, enthält $Input wie erwartet beide Parameter. Warum aber ist die erste Pipelinevariable leer während die zweite Pipelinevariable den korrekten Wert enthält? Ich habe der Doku entnommern, dass ich nur bei Verwendung von Process einen Streamingmodus habe.

    Wo finde ich ein HowTo, aus dem ich erfahre, wie man ein Trace-Command Logfile lesen muss?
    Wenn ich in meinem Log z.B. "ParameterBinding Information: 0 :         Parameter [Port] PIPELINE INPUT ValueFromPipeline NO COERCION" sehe, obwohl ich bei den Parametern ValueFromPipeLine = $True gesetzt habe, kann ich damit nichts anfangen.

    So, das war es erstmal ;)

    Ich danke Euch.

    Gruss

    Thomas

    Montag, 7. April 2014 14:34

Antworten

  • Hallo Thomas!

    ich habe nun verstanden, dass ich ein Objekt erzeugen und in die Pipe geben muss.

    JA!

    Der Inhalt des Objekts muss vom gleichen Datentyp sein. Soweit korrekt?

    Wie meinst du das von welchem gleichen Typ?
    Eine Funktion sollte natürlich bei jedem Auftruf nur die gleiche Sorte von Objekten ausgeben.
    Also immer nur Äpfel und nicht abwechselnd  Äpfel, Birnen oder Eier.

    Für meine Funktionen muss ich nun einen String (den Servernamen) und einen Int (die Portnummer) in die Pipe schicken. Gibt es eine Möglichkeit, dies zu tun?

    JA! Siehe Oben in der Funktion Demo2!

    Wie man in PowerShell Objekte selbst zusammen bauen kann, kannst du hier lesen:
    PowerShell eigene Objekte erstellen Custom Objects
    http://www.admin-source.de/BlogDeu/463/powershell-eigene-objekte-erstellen-custom-objects

    Denn die zweite Funktion, die die Verbindung zum Server aufbaut, benötigt $Server:$Port

    Da man die zweite Funktion, die die Serververbindung aufbaut, auch mit den Parametern -Server und -Port aufrufen kann, möchte ich die Übergabeparameter auf korrekte Syntax prüfen. Deshalb brauche ich den Port als Int.

    Brauchst du nicht!

    Restlicher Erklärungen siehe folgend ...


    Wie in jeder Computer-Sprache muss man wissen wann man mit einem Bestimmten Typen arbeiten muss und wann nicht.
    Es gibt Sprachen die sind sehr streng Typisiert.
    In PowerShell muss man sich fast nie sorgen um Typen machen.
    PowerShell ist eine Dynamische Sprache dort wird der Typ anhand des Inhaltes erkannt oder meistens in den richtigen Typen umgewandelt (gecasted oder casting).

    Hier muss man natürlich die Regeln kennen nachdem Typen Umgewandelt werden oder nicht.

    Wenn du in der PowerShell folgende Zeile eingibst wirst du sehen das PowerShell den String in eine Zahl umwandelt (Ausprobieren!!!):

    123 +"456" # ergibt 579
    # oder
    123 + [String]"456" # ergibt auch 579

    Wenn du in der PowerShell folgende Zeile eingibst wirst du sehen, das PowerShell die Zahl in einen String umwandelt und dann die beiden Strings „zusammenklebt“ (Ausprobieren!!!):


    "456" + 123 # ergibt den String 456123


    Hier versucht  PowerShell den  2. Wert immer in den gleichen Typen umzuwandeln wie der Führende 1. Wert!!!!!!!

    Wenn du eine Funktion Schreibst und bei dem Parameter einen Typ angibst, dann versucht PowerShell das Argument (der Wert) in diesen Typen Umzuwandeln.
    Die Typangabe ist keine Typ Überprüfung, sondern ein Umwandlungswunsch (Casting)!

    Folgenden Code selbst Ausprobieren!!!

    Function Demo3 {
        param(
    
            # Der Inhalt von $Zahl soll in einen Integer umgewandelt werden
     # Wenn das nicht geht wird ein Fehler erzeugt
            [int]$Zahl
        )
    
    
        # ausgabe des Integers
        $Zahl
    
    
    }
    
    # der String wird von der Funktion Demo3 in einen Integer umgewandelt
    Demo3 -Zahl ([String]"123.456")
    
    
    # Fehler der String kann nicht in eine Zahl gewandelt werden
    Demo3 -Zahl ([String]"Hallo!")

    Der Wert "Hallo!" kann nicht in den Typ "System.Int32" konvertiert werden.


    Du hast anscheinend die Seele von Objekten noch nicht verstanden.

    Ein Objekt ist ein Behälter mit einem bestimmten Typ.
    Das Objekt enthält  Daten und Methoden.
    Die Daten (Properties) sind auch wieder Objekte von einem bestimmten Typ.
    Jedes Property (Daten) ist wiederum ein Objekt von einem bestimmten Typ, das Daten und Methoden enthält.
    Diese wiederum können Propertys von einem bestimmten Typ enthalten die wiederum Daten und Methoden enthalten….usw….usw….usw….usw….

    Objekte sind (endlos) ineinander verschachtelte Behälter.
    Daten werden auch Property (Eigenschaft) genannt.
    Daten sind selbst auch wieder Objekte.
    Methoden führen eine Aktion oder eine Berechnung aus und geben als Ergebnis ein Objekt zurück!

    Bei deinen Parameter Validierungen des Integers bist du zu Paranoid! ;-))
    PowerShell wandelt einen String in eine Ganz-Zahl (integer).
    Deshalb reicht die einfache Überprüfung ob diese Zahl innerhalb eines Bereichs (Range) liegt.

    Folgenden Code selbst Ausprobieren!!!

    Function Demo4 {
    # Funktion die den Server mit Port ermittelt
    
        # Ich ermittle den Servernamen
        $ServerName = $env:Computername
    
        # ich ermittel den Port
        $PortNummer = 65535 - 1001
    
    
        # zusammenbauen des Ausgabe Objektes
        New-Object -TypeName PSObject -Property @{Server=$ServerName;Port=$PortNummer}
    
    }
    
    Function Demo5 {
    
        [CmdletBinding()]
        Param(
    
            [parameter( Mandatory = $True,
                Position = 0,
                ValueFromPipeLine = $True,
                ValueFromPipeLineByPropertyName = $True,
               HelpMessage = "Servernamen eingeben! Maximale länge 255, nur Buchstaben und Zahlen."
            )]
            [ValidateLength(1,255)]
            [ValidatePattern("^([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$")]
            [String]$Server,
      
            [parameter(Mandatory = $True,
            Position = 1,
            ValueFromPipeLine = $True,
            ValueFromPipeLineByPropertyName = $True,
            HelpMessage = "Port als Zahl zwischen 1024 und 65535 eingeben"
            )]
            [ValidateRange(1024,65535)]
            [Int]$Port
    
        )
       
        Process {
        
            Write-Host "Ich bearbeite nun: $Server`:$Port" -ForegroundColor Green 
       
        }
    
    }
    
    
    # Aufruf der Funktion Demo5 mit einzelnen Parametern
    Demo5 -Server "Theodor123" -Port 24024
    
    # Aufruf der Funktion Demo5 mit einzelnen Parametern
    # Der Port wird von der Funktion in eine Zahl gewandelt!
    Demo5 -Server Willibald6 -Port "36036"
    
    # Die Funktion Demo4 übergibt die Parameter als Objekt
    # die Parameter werden anhand Ihrer Namen (ValueFromPipeLineByPropertyName) zugewiesen
    Demo4 | Demo5

    eine andere Variante:

    Function Demo6 {
    # Funktion die den Server mit Port ermittelt
    
        # Ich ermittle den Servernamen
        $ServerName = $env:Computername
    
        # ich ermittel den Port
        $PortNummer = 65535 - 1001
    
    
        # zusammenbauen des Ausgabe Objektes
        New-Object -TypeName PSObject -Property @{Text=$ServerName;Nummer=$PortNummer}
    
    }
    
    Function Demo5 {
    
        [CmdletBinding()]
        Param(
    
            [parameter( Mandatory = $True,
                Position = 0,
                ValueFromPipeLine = $True,
                ValueFromPipeLineByPropertyName = $True,
               HelpMessage = "Servernamen eingeben! Maximale länge 255, nur Buchstaben und Zahlen."
            )]
            [ValidateLength(1,255)]
            [ValidatePattern("^([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$")]
            [String]$Server,
      
            [parameter(Mandatory = $True,
            Position = 1,
            ValueFromPipeLine = $True,
            ValueFromPipeLineByPropertyName = $True,
            HelpMessage = "Port als Zahl zwischen 1024 und 65535 eingeben"
            )]
            [ValidateRange(1024,65535)]
            [Int]$Port
    
        )
       
        Process {
        
            Write-Host "Ich bearbeite nun: $Server`:$Port" -ForegroundColor Green 
       
        }
    
    }
    
    
    # Zwischenschritt Nötig, wenn die Propertynamen nicht passen!
    
    # Die Funktion Demo6 übergibt die Parameter als Objekt
    # die Parameter werden von Select-object umbenannt und
    # dann anhand Ihrer Namen (ValueFromPipeLineByPropertyName) zugewiesen
    Demo6 | Select-Object @{Name='Server';Expression={ $_.Text}},@{Name='Port';Expression={ $_.Nummer}} | Demo5


    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    • Bearbeitet Peter Kriegel Donnerstag, 10. April 2014 14:39
    • Als Antwort markiert ThomK99 Freitag, 11. April 2014 08:23
    Donnerstag, 10. April 2014 07:20

Alle Antworten

  • Hallo Thomas!

    Wenn du keine Code Beispiele zeigst reden wir hier wie die Blinden über Farbe und können nur raten.....

    Ich habe eine Funktion, die zwei Werte, einen String und einen Int zurückgibt.

    Etwa so ?

    Function Func1 {
    
    	# 2 Werte werden EINZELN zurückgegeben
    	# ein String 
    	"Hallo Welt !"
    	# und ein Integer
    	1234
    	
    } 

    Funktionen in PowerShell sollten Objekte weitergeben, nicht einzelne Werte.


    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    Montag, 7. April 2014 14:54
  • Hallo,

    hier ein Codebeispiel:

    function Func1 {

      [CmdletBinding()]

      param {}

      begin {}

      process {

        hier wird der Name des Servers und der Netzwerkport ermittelt

      }

      end {
        [String]$server
        [Int]$Port
      }

    }

    function Func2 {

    [CmdletBinding()]

      Param(

      [parameter(
        ParameterSetName = "String",
        Mandatory= $True,
        Position = 0,
        ValueFromPipeLine = $True,
        ValueFromPipeLineByPropertyName = $True,
        HelpMessage = ""
        )
      ]
      [ValidateLength(1,255)]
      [ValidatePattern("^([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$")]
      [String]$Server,
      
      [parameter(
        ParameterSetName = "Int",
        Mandatory= $True,
        Position = 1,
        ValueFromPipeLine = $True,
        ValueFromPipeLineByPropertyName = $True,
        HelpMessage = ""
        )
      ]
      [ValidatePattern("^\d{4,5}$")]
      [ValidateScript({ [int]$_ -gt 1024 })]
      [ValidateScript({ [int]$_ -le 65535 })]
      [Int]$Port

      )

      #Begin {
      $Input
      $Server
      $Port
       
     # Process {

       

      #End {

        # WebServiceProxy erstellen

        }
    Dienstag, 8. April 2014 08:19
  • Hallo Thomas!

    Dir fehlen sehr viele Grundlagen.
    Ich Empfehle dir ein gutes PowerShell Buch zu lesen.
    http://www.powershell-group.eu/buecher-books/

    Das Buch „Windows PowerShell in Action“  Second Edition von Bruce Payette ist für diese Thema am relevantesten.
    Oder in dem Buch : „PowerShell in Depth“ von Don Jones, Richard Siddaway und Jeffrey T. Hicks aus dem Manning Verlag ist das PowerShell Parameter Binding auch sehr gut und ausführlich erklärt.


    Das dumme an PowerShell ist, das eine Funktion alles EINZELN ausspuckt was nicht einer Variablen zugewiesen wird.

    Function Demo1 {
    
        # Jedes der folgenden Objekte wird EINZELN von der Funktion ausgespuckt
        
        # ein String
        "Hallo Welt !"
        
        # und ein Integer
        1234
        
        # eine Datei
        Get-ChildItem $env:windir\explorer.exe
               
    }
    
    # aufruf der Function Demo1
    Demo1 

    1. Cmdlets und Funktionen in PowerShell sollten Objekte weitergeben, nicht einzelne Werte.
    Wenn eine Funktion einzelne Werte ausspuckt habe diese Werte Verschiedene Typen.
    Der nächste Befehl in der Pipeline kann aber immer nur bestimmte Typen verarbeiten, alle anderen Typen werden Ignoriert!

    Das Folgende Kommando in der Pipeline muss also die Typen des Vorhergehenden Kommandos kennen und Verstehen.
    Gute Beispiele von Cmdlet die die Typen des vorhergehenden Cmdlets verstehen sind :

    Get-Process | Stop-Process # hier werden Objekte vom Typ Process verarbeitet
    Get-Service | Stop-Service # hier werden Objekte vom Typ Service verarbeiten
    Get-Child-Item | Remove-Item # hier werden Objekte vom Typ FileInfo oder DirectoryInfo verarbeiten

    Ein Cmdlet oder eine Function sollte immer Objekte ausgeben.
    Die Objekte sollten immer ein und dem Selben Bauplan haben (= Typ oder Klasse), damit das nächste Kommando das Objekt auf immer die gleich weise verarbeiten kann.

    z.B:

    Function Demo2 {
    
        # ein String
        $Text = "Hallo Welt !"
    
        # und ein Integer
        $Zahl = 1234
    
        # eine Datei
        $Datei = Get-ChildItem $env:windir\explorer.exe
    
    
        # Die Werte werden zu einem Objekt "zusammengebunden"
        New-Object -TypeName PsObject -Property @{ Datei=$Datei;Port=$Zahl;Server=$Text }
    
    }
    
    # aufruf der Function Demo1
    Demo2 
    Wie man in PowerShell Objekte selbst zusammen bau kannst du hier lesen:

    PowerShell eigene Objekte erstellen Custom Objects
    http://www.admin-source.de/BlogDeu/463/powershell-eigene-objekte-erstellen-custom-objects

    Das Folgende Kommando in der Pipeline MUSS natürlich so gebaut werden das es dieses Objekt versteht und lesen kann!

    2. Die Ausgaben von einer Advanced Function sollten immer im Process {} Block geschehen, damit sie den Pipeline-Stream nicht unterbrechen und die Objekte sofort weitergereicht werden.
    Wenn alle Cmdlets oder Function sich daran halten Fließen die Objekte gleichmäßig ohne Stau die Pipeline entlang.

    Nur Functions oder Cmdlets die die Objekte in der Pipeline sammeln müssen, geben alle Gesammelten Objekte auf einmal im End{} Block wieder aus.
    Dadurch wird der Stream der Pipeline im Speicher aufgestaut!

    Beispiele solcher Cmdlets die einen Stau verursachen sind z.B. Sort-Object oder Group-Object.

    Das hält den Fluss auf und sollte vermieden werden!

    3. Wenn du die Objekte im Prozess{} Block ausgeben lässt dann werden Sie einzeln Nacheinander ausgegeben.
    Wenn du die  Objekte im End{} Ausgeben lässt dann werden sie gemeinsam ausgegeben!

    Hier kommt der arme PowerShell Parameter Binder völlig durcheinander!
    Deine erste Funktion Liefert ein Argument von Typ STRING und ein Argument vom Typ INTEGER gleichzeitig.
    Wie soll der Parameter-Binder nun welches Argument an einen Parameter binden?

    Auszug aus dem Buch „PowerShell in Action“ von Bruce Payette

    PowerShell Parameter Binding steps Description

    1. Bind all named parameters. Find all unquoted tokens on the command line that start with a
    dash. If the token ends with a colon, an argument is required. If
    there’s no colon, look at the type of the parameter and see if an
    argument is required. Convert the type of actual argument to the
    type required by the parameter, and bind the parameter.
    2. Bind all positional parameters. If there are any arguments on the command line that haven’t
    been used, look for unbound parameters that take positional
    parameters and try to bind them.
    3. Bind from the pipeline by value
    with exact match.
    If the command is not the first command in the pipeline and
    there are still unbound parameters that take pipeline input, try to
    bind to a parameter that matches the type exactly.
    4. If not bound, then bind from
    the pipe by value with conversion.
    If the previous step failed, try to bind using a type conversion.
    5. If not bound, then bind from
    the pipeline by name with exact
    match.
    If the previous step failed, look for a property on the input object
    that matches the name of the parameter. If the types exactly
    match, bind the parameter.
    6. If not bound, then bind from
    the pipeline by name with conversion.
    If the input object has a property whose name matches the
    name of a parameter, and the type of the property is

    Links:
    Pipeline Parameter Binding: ByValue
    http://windowsitpro.com/blog/pipeline-parameter-binding-byvalue

    Pipeline Parameter Binding: ByPropertyName
    http://windowsitpro.com/blog/pipeline-parameter-binding-bypropertyname

    Troubleshooting Pipeline Parameter Binding by Peeking Inside
    http://powershell.com/cs/blogs/donjones/archive/2012/07/31/troubleshooting-pipeline-parameter-binding-by-peeking-inside.aspx

    4. Du hast die Seele und den Sinn von Parametersets noch nicht voll erfasst.
    So wie du die Parametersets einsetzt dafür sind sie nicht gedacht!


    Genau dafür sind Parametersets gedacht!

    Sobald ich die zwei Parameter der zweiten Funktion mit ParameterSetName definiere (einmal String, einmal Int) und diese Funktion in der ISE Commandline mit manuell angegebenen Parametern aufrufen möchte, habe ich das Problem, dass sobald ich den ersten Parameter angebe, der zweite Parameter nicht mehr in der Auswahlliste, die ich durch Eingabe des - erhalte, erscheint.

    Ein Parameterset schließt das andere aus!

    In einem Parameterset und dem angelieferten Objekt muss es einen Führenden Parameter (Property) geben, mit einem Typen, der in keinem anderen Parameterset vorkommt.
    Damit der PowerShell Parameterbindern erkennen kann an welches Set er das Objekt binden soll.

    Hat er das Erkannt werden die anderen Sets ausgeschlossen (ausgeblendet).

    Wnn du der Funktion einen String zuführst wird der Integer ausgeblendet weil er nur im Set Int existiert.

    Ebenso sollte man in dem Attribut CmdletBinding() angeben welches Parameterset das Default Set ist!

    Links:
    PowerShell (v2) - ParameterSets for Dummies
    http://learningpcs.blogspot.de/2012/06/powershell-v2-parametersets-for-dummies.html

    PowerShell V2: ParameterSets
    http://blogs.msdn.com/b/powershell/archive/2008/12/23/powershell-v2-parametersets.aspx

    All das steht in den erwähnten Büchern !

                               

    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+




    Dienstag, 8. April 2014 11:13
  • Hallo Peter,

    vielen Dank für Deine ausführliche Antwort.

    Klar hast Du Recht, dass ich im Bezug auf Powershell Scripting ein Neuling bin. Ich bin gerade dabei, das Scripten mit der PS zu lernen.

    Dazu arbeite ich das Buch "Scripting mit Windows Powershell 2.0" des Herrn Weltner durch. Parallel dazu nutze ich die Technet "about_" Doku und google :) Allerdings habe ich konkreten Bedarf, so dass ich nicht die Beispiele des Buchs abtippe, sondern die Informationen direkt in meine konkrete Aufgabenstellung einbinde.

    Ich lese mir gleich Deinen Artikel zu den Custom Objects und zu Parametersets durch, um dies besser verstehen zu können.

    Melde mich dann wieder, wenn noch Fragen offen sind.

    Gruss

    Thomas

    Dienstag, 8. April 2014 12:32
  • Hallo Peter,

    ich habe nun verstanden, dass ich ein Objekt erzeugen und in die Pipe geben muss. Der Inhalt des Objekts muss vom gleichen Datentyp sein. Soweit korrekt?

    Für meine Funktionen muss ich nun einen String (den Servernamen) und einen Int (die Portnummer) in die Pipe schicken. Gibt es eine Möglichkeit, dies zu tun?

    Denn die zweite Funktion, die die Verbindung zum Server aufbaut, benötigt $Server:$Port

    Da man die zweite Funktion, die die Serververbindung aufbaut, auch mit den Parametern -Server und -Port aufrufen kann, möchte ich die Übergabeparameter auf korrekte Syntax prüfen. Deshalb brauche ich den Port als Int.

    Danke und Gruss

    Thomas

    Mittwoch, 9. April 2014 16:54
  • Hallo Thomas!

    ich habe nun verstanden, dass ich ein Objekt erzeugen und in die Pipe geben muss.

    JA!

    Der Inhalt des Objekts muss vom gleichen Datentyp sein. Soweit korrekt?

    Wie meinst du das von welchem gleichen Typ?
    Eine Funktion sollte natürlich bei jedem Auftruf nur die gleiche Sorte von Objekten ausgeben.
    Also immer nur Äpfel und nicht abwechselnd  Äpfel, Birnen oder Eier.

    Für meine Funktionen muss ich nun einen String (den Servernamen) und einen Int (die Portnummer) in die Pipe schicken. Gibt es eine Möglichkeit, dies zu tun?

    JA! Siehe Oben in der Funktion Demo2!

    Wie man in PowerShell Objekte selbst zusammen bauen kann, kannst du hier lesen:
    PowerShell eigene Objekte erstellen Custom Objects
    http://www.admin-source.de/BlogDeu/463/powershell-eigene-objekte-erstellen-custom-objects

    Denn die zweite Funktion, die die Verbindung zum Server aufbaut, benötigt $Server:$Port

    Da man die zweite Funktion, die die Serververbindung aufbaut, auch mit den Parametern -Server und -Port aufrufen kann, möchte ich die Übergabeparameter auf korrekte Syntax prüfen. Deshalb brauche ich den Port als Int.

    Brauchst du nicht!

    Restlicher Erklärungen siehe folgend ...


    Wie in jeder Computer-Sprache muss man wissen wann man mit einem Bestimmten Typen arbeiten muss und wann nicht.
    Es gibt Sprachen die sind sehr streng Typisiert.
    In PowerShell muss man sich fast nie sorgen um Typen machen.
    PowerShell ist eine Dynamische Sprache dort wird der Typ anhand des Inhaltes erkannt oder meistens in den richtigen Typen umgewandelt (gecasted oder casting).

    Hier muss man natürlich die Regeln kennen nachdem Typen Umgewandelt werden oder nicht.

    Wenn du in der PowerShell folgende Zeile eingibst wirst du sehen das PowerShell den String in eine Zahl umwandelt (Ausprobieren!!!):

    123 +"456" # ergibt 579
    # oder
    123 + [String]"456" # ergibt auch 579

    Wenn du in der PowerShell folgende Zeile eingibst wirst du sehen, das PowerShell die Zahl in einen String umwandelt und dann die beiden Strings „zusammenklebt“ (Ausprobieren!!!):


    "456" + 123 # ergibt den String 456123


    Hier versucht  PowerShell den  2. Wert immer in den gleichen Typen umzuwandeln wie der Führende 1. Wert!!!!!!!

    Wenn du eine Funktion Schreibst und bei dem Parameter einen Typ angibst, dann versucht PowerShell das Argument (der Wert) in diesen Typen Umzuwandeln.
    Die Typangabe ist keine Typ Überprüfung, sondern ein Umwandlungswunsch (Casting)!

    Folgenden Code selbst Ausprobieren!!!

    Function Demo3 {
        param(
    
            # Der Inhalt von $Zahl soll in einen Integer umgewandelt werden
     # Wenn das nicht geht wird ein Fehler erzeugt
            [int]$Zahl
        )
    
    
        # ausgabe des Integers
        $Zahl
    
    
    }
    
    # der String wird von der Funktion Demo3 in einen Integer umgewandelt
    Demo3 -Zahl ([String]"123.456")
    
    
    # Fehler der String kann nicht in eine Zahl gewandelt werden
    Demo3 -Zahl ([String]"Hallo!")

    Der Wert "Hallo!" kann nicht in den Typ "System.Int32" konvertiert werden.


    Du hast anscheinend die Seele von Objekten noch nicht verstanden.

    Ein Objekt ist ein Behälter mit einem bestimmten Typ.
    Das Objekt enthält  Daten und Methoden.
    Die Daten (Properties) sind auch wieder Objekte von einem bestimmten Typ.
    Jedes Property (Daten) ist wiederum ein Objekt von einem bestimmten Typ, das Daten und Methoden enthält.
    Diese wiederum können Propertys von einem bestimmten Typ enthalten die wiederum Daten und Methoden enthalten….usw….usw….usw….usw….

    Objekte sind (endlos) ineinander verschachtelte Behälter.
    Daten werden auch Property (Eigenschaft) genannt.
    Daten sind selbst auch wieder Objekte.
    Methoden führen eine Aktion oder eine Berechnung aus und geben als Ergebnis ein Objekt zurück!

    Bei deinen Parameter Validierungen des Integers bist du zu Paranoid! ;-))
    PowerShell wandelt einen String in eine Ganz-Zahl (integer).
    Deshalb reicht die einfache Überprüfung ob diese Zahl innerhalb eines Bereichs (Range) liegt.

    Folgenden Code selbst Ausprobieren!!!

    Function Demo4 {
    # Funktion die den Server mit Port ermittelt
    
        # Ich ermittle den Servernamen
        $ServerName = $env:Computername
    
        # ich ermittel den Port
        $PortNummer = 65535 - 1001
    
    
        # zusammenbauen des Ausgabe Objektes
        New-Object -TypeName PSObject -Property @{Server=$ServerName;Port=$PortNummer}
    
    }
    
    Function Demo5 {
    
        [CmdletBinding()]
        Param(
    
            [parameter( Mandatory = $True,
                Position = 0,
                ValueFromPipeLine = $True,
                ValueFromPipeLineByPropertyName = $True,
               HelpMessage = "Servernamen eingeben! Maximale länge 255, nur Buchstaben und Zahlen."
            )]
            [ValidateLength(1,255)]
            [ValidatePattern("^([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$")]
            [String]$Server,
      
            [parameter(Mandatory = $True,
            Position = 1,
            ValueFromPipeLine = $True,
            ValueFromPipeLineByPropertyName = $True,
            HelpMessage = "Port als Zahl zwischen 1024 und 65535 eingeben"
            )]
            [ValidateRange(1024,65535)]
            [Int]$Port
    
        )
       
        Process {
        
            Write-Host "Ich bearbeite nun: $Server`:$Port" -ForegroundColor Green 
       
        }
    
    }
    
    
    # Aufruf der Funktion Demo5 mit einzelnen Parametern
    Demo5 -Server "Theodor123" -Port 24024
    
    # Aufruf der Funktion Demo5 mit einzelnen Parametern
    # Der Port wird von der Funktion in eine Zahl gewandelt!
    Demo5 -Server Willibald6 -Port "36036"
    
    # Die Funktion Demo4 übergibt die Parameter als Objekt
    # die Parameter werden anhand Ihrer Namen (ValueFromPipeLineByPropertyName) zugewiesen
    Demo4 | Demo5

    eine andere Variante:

    Function Demo6 {
    # Funktion die den Server mit Port ermittelt
    
        # Ich ermittle den Servernamen
        $ServerName = $env:Computername
    
        # ich ermittel den Port
        $PortNummer = 65535 - 1001
    
    
        # zusammenbauen des Ausgabe Objektes
        New-Object -TypeName PSObject -Property @{Text=$ServerName;Nummer=$PortNummer}
    
    }
    
    Function Demo5 {
    
        [CmdletBinding()]
        Param(
    
            [parameter( Mandatory = $True,
                Position = 0,
                ValueFromPipeLine = $True,
                ValueFromPipeLineByPropertyName = $True,
               HelpMessage = "Servernamen eingeben! Maximale länge 255, nur Buchstaben und Zahlen."
            )]
            [ValidateLength(1,255)]
            [ValidatePattern("^([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$")]
            [String]$Server,
      
            [parameter(Mandatory = $True,
            Position = 1,
            ValueFromPipeLine = $True,
            ValueFromPipeLineByPropertyName = $True,
            HelpMessage = "Port als Zahl zwischen 1024 und 65535 eingeben"
            )]
            [ValidateRange(1024,65535)]
            [Int]$Port
    
        )
       
        Process {
        
            Write-Host "Ich bearbeite nun: $Server`:$Port" -ForegroundColor Green 
       
        }
    
    }
    
    
    # Zwischenschritt Nötig, wenn die Propertynamen nicht passen!
    
    # Die Funktion Demo6 übergibt die Parameter als Objekt
    # die Parameter werden von Select-object umbenannt und
    # dann anhand Ihrer Namen (ValueFromPipeLineByPropertyName) zugewiesen
    Demo6 | Select-Object @{Name='Server';Expression={ $_.Text}},@{Name='Port';Expression={ $_.Nummer}} | Demo5


    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    • Bearbeitet Peter Kriegel Donnerstag, 10. April 2014 14:39
    • Als Antwort markiert ThomK99 Freitag, 11. April 2014 08:23
    Donnerstag, 10. April 2014 07:20
  • Hallo Peter,

    ist ja echt der Hammer, wie schnell UND umfangreich Du hier antwortest.

    Das mit der Paranoia werde ich in der nächsten Sitzung mit meinem Psych besprechen ;)

    So wie ich es sehe, brauche ich mir bei Powershell nur sehr wenige Gedanken über das Typisieren zu machen.
    Mein Script funktioniert nun auch. Habe mir Deinen Youtube Beitrag zu Objekten angesehen. Echt gute Vermittlung der Grundlagen.

    Ich habe nun noch eine Frage.
    Eine Prüfung des ParameterBindings mit trace-command gibt mir folgende Zeilen aus:

    DEBUG: ParameterBinding Information: 0 :                     ERROR: DATA GENERATION: Der Wert "@{Server=hanswurst.abc.de; Port=10000}" kann nicht in den Typ "System.Int32" konvertiert werden. Fehler: "Der Wert "@{Server=hanswurst.abc.de; Port=10000}" vom Typ "System.Management.Automation.PSCustomObject" kann nicht in den Typ "System.Int32" konvertie
    rt werden."
    DEBUG: ParameterBinding Information: 0 :             Parameter [Port] PIPELINE INPUT ValueFromPipelineByPropertyName WITH COERCION

    Weshalb nicht umgewandelt werden kann, weiss ich. Möchte nur gerne wissen, ob die Meldung eher als Warnung/Hinweis zu sehen ist oder ob ich an meinem Script noch etwas ändern muss.

    Danke.

    Thomas


    Donnerstag, 10. April 2014 10:17
  • Der Parameterbinder versucht im Hintergrund viele sachen, unter anderem auch das Casten, wenn das casten nicht Funktioniert versucht er was anderes das auch wieeder Shief gehen könnte usw...

    Wichtig ist zum schluss ob der Binder eine Methode gefunden hat alle Parameter zu binden.

    Normalerweise benutzt man das Trace Kommando ja nicht und keiner sieht wie der Binder sich abstrampelt...

    Also wenn du keine Rote Fehlermeldung bekommst, wenn du den Befehl ohne Trace anwendest, ist doch alles in Butter!




    PowerShell Artikel, Buchtipps und kostenlose PowerShell Tutorials + E-Books
    auf der deutschsprachigen PowerShell Community

    Mein 21 Teiliger PowerShell Video Grundlehrgang
    Deutsche PowerShell Videos auf Youtube
    Folge mir auf:
    Twitter | Facebook | Google+

    Donnerstag, 10. April 2014 14:57