Getting confused with simple piping semantics

Tagged: 

This topic contains 5 replies, has 5 voices, and was last updated by Profile photo of Wei-Yen Tan Wei-Yen Tan 6 months ago.

  • Author
    Posts
  • #41754
    Profile photo of Wei-Yen Tan
    Wei-Yen Tan
    Participant

    I think I am confusing myself with some piping semantics.

    I am playing around with Get-ADComputer and want to pass it a few computers to check through the pipeline.

    So this is how it looks.

    'Server01' | get-adcomputer | export-clixml 'c:\reports\xml\$_.xml'
    

    Basically I want that computer name to appear as the file name.

    I thought I could pass the name through but it looks like it didn't go through...Its been coming as blank. Any ideas? TIA

  • #41758
    Profile photo of Chris Bakker
    Chris Bakker
    Participant

    Probably it needs an object from the pipline, your putting in a string.
    Cant test in, dont have ad, but this exmple should be simular:

    PS C:\Users> Get-Process 'firefox'

    Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id SI ProcessName
    ——- —— —– —– —– —— — — ———–
    660 48 217676 211960 528 35,66 3336 1 firefox

    PS C:\Users>'firefox'|Get-Process
    Get-Process : The input object cannot be bound to any parameters for the comman
    d either because the command does not take pipeline input or the input and its
    properties do not match any of the parameters that take pipeline input.
    At line:1 char:12
    + 'firefox'|Get-Process
    + ~~~~~~~~~~~
    + CategoryInfo : InvalidArgument: (firefox:String) [Get-Process],
    ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Command
    s.GetProcessCommand

    PS C:\Users> $fire= Get-Process 'firefox'

    PS C:\Users> $fire|Get-Process

    Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id SI ProcessName
    ——- —— —– —– —– —— — — ———–
    628 34 220488 215880 530 39,73 3336 1 firefox

    PS C:\Users>

  • #41760
    Profile photo of Wes Stahler
    Wes Stahler
    Participant

    Chris is correct. Try something like this:

    'server1','server2' | ForEach-Object {
        $server= Get-ADComputer $PSItem
        $file = "C:\temp\$($server.name).xml"
        Export-Clixml -InputObject $server -Path $file 
    }
    
    • This reply was modified 6 months ago by Profile photo of Wes Stahler Wes Stahler.
  • #41763
    Profile photo of random commandline
    random commandline
    Participant

    This should work. Using Get-Help will show you if a parameter accepts pipeline input.

    $servers = 'Server01'
    foreach ($server in $servers){get-adcomputer | export-clixml "c:\reports\xml\$server.xml"}
    
  • #41822
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    Hey @wei-yen-tan,
    I think I'm reading your question a little different than the rest of the folks that have replied so far. As I read it the question is not so much about the command being used, but rather how the pipeline works and why $_ does not represent the name of the server you passed in at the beginning of the pipeline in the below oneliner:

    'Server01' | get-adcomputer | export-clixml 'c:\reports\xml\$_.xml'
    

    You are expecting this to result in the export-clixml command being

    export-clixml 'c:\reports\xml\Server01.xml'
    

    There are two reasons for why $_ does not resolve to Server01 in the above command.

    1) The use of single quotes.
    When single quotes are used, variables, such as $_, are not resolved to their content; rather, they are taken as literal characters. As a result your command ends up being literally

    export-clixml 'c:\reports\xml\$_.xml'
    

    If you want $_ to resolve to the variable value, used double quotes instead. In the following command, $_ will be replace with the value of the variable before the command is executed:

    export-clixml "c:\reports\xml\$_.xml"
    

    2) The second issue is what is expected to be in the $_ variable at the point in the pipeline that it is being used. Let me lay it out like this. The pipe symbol "|" in the commandline represents the pipeline. The command before the | is putting data into the pipeline. The command after the | is taking data from the pipeline. Let me lay out how this workes using the provided sample oneliner.

    'Server01' | get-adcomputer | export-clixml 'c:\reports\xml\$_.xml'
    

    1) The first command: 'Server01' outputs a literal string Server01
    2) This string Server01 is sent to the Pipeline to hold until it is passed to the next commandlet
    3) The first object in the pipeline, in this case Server01, is sent to the next command in the onliner
    4) The second command: get-adcomputer uses Server01 as input and outputs an object represending the computer object in AD
    5) This computer object is sent to the Pipeline to hold until it is passed to the next commandlet. ***Note: The pipeline at this point is not holding a string with a value of Server01. It is holding an object with all the data it pulled from Active Directory. By default, this includes data such as DistinguishedName, DNSHostName, Name, ObjectClass, etc, etc.***
    6) The first object in the pipeline, in this case the Object containing all of the data for Server01 from AD is sent to the next command in the oneliner
    7) The third command: export-clixml 'c:\reports\xml\$_.xml' uses the Object from the pipeline as input as well as tries to use the $_, which represents the current object from the pipeline when used with a loop, as the name of the file to export to. Since no commandlet that loops is being used, such as foreach-object, the $_ variable is blank.

    #From about_Automatic_Variables
    $_
           Same as $PSItem. Contains the current object in the pipeline object.
           You can use this variable in commands that perform an action on every
           object or on selected objects in a pipeline.
    

    When foreach-object is added to the oneliner, like below, $_ represents the current object from the pipeline as each object from the pipeline is processed

    'Server01' | get-adcomputer | ForEach-Object {export-clixml -InputObject $_ -Path "c:\reports\xml\$_.xml"}
    

    Another problem is what $_ represents

    An example of the object representation is below:

    @'
    DistinguishedName : CN=SERVER1,CN=Computers,DC=ca,DC=lab
    DNSHostName       : Server1.ca.lab
    Enabled           : True
    Name              : SERVER1
    ObjectClass       : computer
    ObjectGUID        : fa78f3b3-0458-4a39-ab41-5e1f13ac40c9
    SamAccountName    : SERVER1$
    SID               : S-1-5-21-607888948-3061123872-308631077-1143
    UserPrincipalName : 
    '@

    Obviously you cannot have a filename that looks like the above + .xml

    Fortunately when a computer object producted by Get-AdComputer is resolved as a string, it outputs the DistringuishedName Property as a string.

    So the result of:

    'Server01' | get-adcomputer | ForEach-Object {export-clixml -InputObject $_ -Path "c:\reports\xml\$_.xml"}

    will be a file called something like:

    CN=SERVER01,CN=Computers,DC=ca,DC=lab.xml

    Which is not your desired result.

    What you want is a specific piece of data from that object. For this you need to use a subexpression to get the desired property from the current object that is represented by $_

    export-clixml "c:\reports\xml\$($_.Name).xml"
    

    What the above does is tell PowerShell to interpret variables due to the use of double-quotes. Additionally, the $() tells PowerShell that there is an expression between the parenthesis that needs to be processed before plugging it's value into the string. In this case it is to read the value of the Name property from the Current Object received from the pipeline.

    $_.name equals SERVER01 as shown above, so the command resolves out to the following before executing

    export-clixml "c:\reports\xml\SERVER1.xml"

    'Server01' | get-adcomputer | ForEach-Object {export-clixml -InputObject $_ -Path "c:\reports\xml\$($_.Name).xml"}
    

    This, of course, will work with multiple server names passed in at the beginning

    'Server01', 'Server02' | get-adcomputer | ForEach-Object {export-clixml -InputObject $_ -Path "c:\reports\xml\$($_.Name).xml"}
    
  • #41985
    Profile photo of Wei-Yen Tan
    Wei-Yen Tan
    Participant

    Thanks for that @Curtis Smith for the explanation. This exactly what I was looking for. I was actually looking at a problem that I was having with one of my own custom objects(collections) and after you have explained how piping works I know where the fault lies.

    Thanks for the time you spent with my on parsing sql. I sat down and worked out how it worked and got the relevant information. It is working beautifully,

You must be logged in to reply to this topic.