Splitting a string, ForEach-Object

This topic contains 9 replies, has 4 voices, and was last updated by  camelCreed 1 week ago.

  • Author
    Posts
  • #81779

    camelCreed
    Participant

    Hello,

    I am feeding and imported csv to a ForEach-Object loop and am confused about the result.

    Each object in the collection has a property called 'LAN Hostnames'. I need to remove everything after the first comma in this properties value on each object.

    Example Objects:

    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name1, name1, name1
    OS Detail: Windows NT; Windows NT; Windows NT

    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name2, name2, name2, name2, name2
    OS Detail: Windows NT; Windows NT; Windows NT

    I am trying to end up with:

    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name1
    OS Detail: Windows NT

    Here is something I have tried...

    $import = Import-CSV -path E:\folder\file.csv
    
    $split = ForEach ($i in $import) {
    
    $_.'LAN Hostnames'.Split(',')[0]
    
    }

    I thought this would iterate through the collection of objects and split the 'LAN Hostnames' value at the comma. Which it does, but $split contains only these values. $split is...

    name1
    name2

    ...etc. I am excited to learn about working with strings in this way. Any advice appreciated.

  • #81784

    Jeremy Murrah
    Participant

    so you want to modify the value of the 'LAN Hostname' property but otherwise leave the rest of the output intact?

    There are a couple of ways to do that, probably the most readable of which is to craft a new object within your loop. You're on the right track with your string manipulation code, but since that's all you're outputting from your loop, that's all you're going to get in your variable. You need to output the other properties of $i along with your modified 'LAN Hostnames' value. Try this:

    $import = Import-CSV -path E:\folder\file.csv
    
    ForEach ($i in $import) {
    
        new-object PSObject -Property @{
            'Device Type' = $i.'Device Type'
            'OS' = $i.OS
            'LAN Hostname' = $i.'LAN Hostnames'.Split(',')[0]
            'OS Detail' = $i.'OS Detail'
        }
    }
    

    You could also do it with calculated properties all on one line, but the above should be alot more readable.

    • #81799

      camelCreed
      Participant

      Jeremy,

      This solution of creating custom objects worked beautifully. It is exactly what I needed, and makes sense! I will definitely carry this with me going forward.

      Thank you for your time!

    • #81844

      Naw Awn
      Participant

      This my lazy way 😛 if all has the same name

      'LAN Hostname' = $i.'LAN Hostnames' | Select -Unique
      'OS Detail' = $i.'OS Detail' | Select -Unique
      
    • #81845

      Curtis Smith
      Participant

      Hey Naw,
      The problem there is that the value is a string not an array where Select-Object -Unique can compare multiple string and only return the unique elements.

    • #81851

      Naw Awn
      Participant

      Oh, Thanks for pointing that out. I thought it was a returned object from somewhere. Yes, if it's the string I would go with .split(",") or if all those string has the same length .SubString(0,5) for namex etc.,

  • #81787

    Curtis Smith
    Participant

    Hey

    Your first problem here, based on your example data, is that your inputfile is not really a .csv file. It's a text file where one of the lines happens to be comma delimited. If you output $import to the prompt, you will see why this is a problem.

    $import = Import-CSV -path "D:\input.csv"
    $import

    Results

    Device Type: Windows 10                      
    -----------------------                      
    OS: Windows                                  
    LAN Hostname: name1                          
    OS Detail: Windows NT; Windows NT; Windows NT

    As you can see from the above results, it is using the first line as a column header, which it is not, and then making subsequent lines values of that header.

    So first you need to get the content of the file in with the correct method. In this case it's just a text file.

    $import = Get-Content -path "D:\input.csv"
    $import

    Results

    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name1, name1, name1
    OS Detail: Windows NT; Windows NT; Windows NT

    Now you can go through each line and do whatever manipulation you want. Your second problem is that you are using $_ to reference the current object in the foreach loop. This would be OK if you were Piping the output of Get-Content to ForEach-Object, but you are not. You are using ForEach and defining $i as the current object, so you need to use $i as your reference variable.

    $import = Get-Content -path "D:\input.csv"
    $split = ForEach ($i in $import) {
        $i.Split(',|;')[0]
    }
    $split

    Results

    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name1
    OS Detail: Windows NT

    For an example using Pipe and $_, you could do this.

    $split = Get-Content -path "D:\input.csv" |
    ForEach-Object {
        $_.Split(',|;')[0]
    }
    $split

    Same Results

    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name1
    OS Detail: Windows NT
    • #81793

      camelCreed
      Participant

      Hi Curtis!

      I am not sure how I ended up typing $_. in my loop above. I am using $i.'LAN Hostnames' in the script. I just mis-typed that, but good eye.

      Also, when I import my csv, the results are not as you have shown above. The result of Import-CSV is exactly how I posted above...

      Device Type: Windows 10
      OS: Windows
      LAN Hostname: name1, name1, name1
      OS Detail: Windows NT; Windows NT; Windows NT

      Device Type: Windows 10
      OS: Windows
      LAN Hostname: name2, name2, name2, name2, name2
      OS Detail: Windows NT; Windows NT; Windows NT

      The Property side on the left shows the column header names in the csv (Device Type, OS, etc...) and the values shown are separate rows in the .csv file.

      I will try using Get-Content instead and see how it goes.

      Thank you!

  • #81830

    Curtis Smith
    Participant

    Ok, this is likely me missunderstanding what the data you provided represented.

    I understood:
    Device Type: Windows 10
    OS: Windows
    LAN Hostname: name1, name1, name1
    OS Detail: Windows NT; Windows NT; Windows NT

    as being the content of your input file; however, based on your last statement, the content of your input file is actually something like.

    Device Type,OS,LAN Hostname,OS Detail
    Windows 10,Windows,"name1, name1, name1",Windows NT; Windows NT; Windows NT

    If that's the case you do in fact have a CSV and Jeremy's code is a good example, but you don't really need to create a new custom object. You can just use the current object like this.

    Import-Csv -Path "E:\folder\file.csv" |
    ForEach-Object {
        $_.'LAN Hostname' = $_.'LAN Hostname'.split(",")[0]
        $_.'OS Detail' = $_.'OS Detail'.split(";")[0]
        $_
    } |
    Format-List

    Results

    Device Type  : Windows 10
    OS           : Windows
    LAN Hostname : name1
    OS Detail    : Windows NT
    • #81854

      camelCreed
      Participant

      Hi Curtis,

      Correct, that is what the .csv looks like. And I will take note and write out your example as well.

      Thank you!

You must be logged in to reply to this topic.