سؤال

Suppose I have objects that have a parent-child relationship like this:

public class Node
{
   public string Name { get; set; }
   public string Type { get; set; }
   public Node Parent { get; set; }
}

Now, I would like to create a cmdlet that supports syntax like this:

Get-Node | where {$_.Type -eq "SomeType" -and $_.Parent.Name -eq "SomeName" }

Here, the Parent property needs to somehow reference another object in the pipeline. Is something like this even possible in PowerShell? If not, what are the alternatives?

[Edit] If I use the class above like this:

var root = new Node
{
    Name = "root",
    Type = "root",
    Parent = null
};
var nodeA = new Node
{
    Name = "A",
    Type = "node",
    Parent = root
}
WriteObject(root);
WriteObject(nodeA);

And then load the module and try this command:

Get-MyNode | where {$_.Parent.Name = "root"}

I get this error:

Property 'Name' cannot be found on this object; make sure it exists and is settable.
At line:1 char:31
+ Get-MyNode | where {$_.Parent. <<<< Name = "root"}
    + CategoryInfo          : InvalidOperation: (Name:String) [], RuntimeException
    + FullyQualifiedErrorId : PropertyNotFound

I would like the Parent property to reference another object in the pipeline like a real Node object.

[Edit] This error was caused by the public keyword missing from the class definition. Adding the keyword fixed the issue and made the example work.

هل كانت مفيدة؟

المحلول

I would expect that your Get-Node cmdlet would return a fully populated object graph. Here's an similar example of this using XML:

$xml = [xml]@'
<?xml version="1.0" encoding="ISO-8859-1"?>
 <bookstore>
     <book>
       <title lang="eng">Harry Potter</title>
       <price>29.99</price>
     </book>
     <book>
       <title lang="eng">Learning XML</title>
       <price>39.95</price>
     </book>
 </bookstore> 
'@

$xml.SelectNodes('//*') | Where {$_.ParentNode.Name -eq 'book'}

In answer to your question about accessing another object in the pipeline, it isn't uncommon to create intermediate variables as part of the pipeline that can be referenced later:

Get-Process | Foreach {$processName = $_.Name; $_.Modules} | 
              Foreach {"$processName loaded $($_.ModuleName)"}

In this scenario I stash the System.Diagnostics.Process object's name before propagating a completely different type down the pipeline ie System.Diagnostic.ProcessModule. Then I can combine the stashed process name with the module's name to produce the output I want.

The above approach is good for pedagogical purposes but isn't really canonical PowerShell. This would be a more typical way to do this in PowerShell:

Get-Process | Select Name -Exp Modules | Foreach {"$($_.Name) loaded $($_.ModuleName)"}

In this scenario we've taken the Process's name and projected it into each ProcessModule object. Note that some of the processes will generate an error when you try to enumerate their modules collection.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top