Parsing a text file in PowerShell

2020-05-09 18:31发布

问题:

I have a text file with multiple users (in the exact format):

username:t-Hancock

phoneNumber: 555555555

username:a-smith

PhoneNumber: 987654321

username:r-Byer

phonenumber: 123456789

I am trying to get all these users into an object with two properties (name and phone number). I tried Get-Content, tried some regex from other posts, but I couldn't modify them because I didn't understand it.

How could this be done?

回答1:

It's not that clear entirely what you want here - what kind of object and for what purpose?

I'll go with an example of what you could do...

Say you want to convert that text file into a CSV file for easy import into Excel, start with reading the file content:

$input = Get-Content C:\TextFile.txt

Now, create an array into which you will store each object you create:

$array = @()

Now, we will go into a loop, parsing the file contents. If the line starts with "username", split the line using colon as delimiter, grab the second item (0-relative so the second item is number one) into a variable.

If the line starts with "PhoneNumber" then set the $writeobj variable to true (reset to false at the start of each iteration of the loop) and store the "PhoneNumber" value in $PhoneNumber using split again.

Then check if $writeobj is true and, if so, create a new object, add a NoteProperty with name of Username and username value stored from a previous line in the file.

Then add a NoteProperty with name PhoneNumber and value of $PhoneNumber. Then, add the object to the array.

This repeats all the way through the file contents:

$input | foreach-object {
    $writeobj = $false
    $obj = New-Object System.Object
    If ($_ -match 'username*') {
        $Username = ($_ -split ':')[1]
    }
    If ($_ -match 'PhoneNumber*') {
        $PhoneNumber = ($_ -split ':')[1]
        $writeobj = $true
    }
    If ($writeobj){
        $obj | Add-Member -type NoteProperty -name Username -value $Username
        $obj | Add-Member -type NoteProperty -name PhoneNumber -value $PhoneNumber
        $array += $obj
    }
}

Once we're out of the loop, export the array to a CSV file:

$array | Export-Csv -path C:\test.csv -NoTypeInformation

So, the full script is:

$input = Get-Content C:\TextFile.txt
$array = @()
$input | foreach-object {
    $writeobj = $false
    $obj = New-Object System.Object
    If ($_ -match 'username*') {
        $Username = ($_ -split ':')[1]
    }
    If ($_ -match 'PhoneNumber*') {
        $PhoneNumber = ($_ -split ':')[1]
        $writeobj = $true
    }
    If ($writeobj){
        $obj | Add-Member -type NoteProperty -name Username -value $Username
        $obj | Add-Member -type NoteProperty -name PhoneNumber -value $PhoneNumber
        $array += $obj
    }
}
$array | Export-Csv -path C:\test.csv -NoTypeInformation

See the screenshot below of a CSV file opened in Excel.



回答2:

I suggest you to change your format to CSV instead, and then it's very trivial. Just use Import-Csv.

Right now you could do something a long of the lines, it assumes valid text file, no empty lines:

$content = Get-Content "C:\Users\You\Documents\test.txt"
$readUsername = $true
$data = @()
foreach ($line in $content)
{
    if($readUsername -eq $true) {
      $username = .. # Match the line against regex.
      $readUsername = $false
    } else {
       $phone = .. # Match the line against regex
       $readUsername = $true

       # We have grabbed username & phone
       $props = @{Name: $username, Phone: $phone}
       $obj = New-Object PSObject –Property $props
       $data += $obj
    }
}

# You now have access to $data.


回答3:

I would use regexes to get at the data (example below), and you may need to tweak the regexs to extract the names and numbers, because I only had your small sample to go on and I have made some assumptions like no spaces in the names and numbers.

((Get-Content file.txt -Raw) -split '\n(?=username)') | % {
    $x = $_ -split '\r'
    New-Object PSOBJECT -Property @{
        name  = [regex]::Match($x[0],'(?<=username:\s*)\b.*\b')
        phone = [regex]::Match($x[1],'(?<=[Pp]hone[Nn]umber:\s*)\b.*\b')
    }
}