4

Consider the following string:

$foo = @"
cfa7b63c88ed1eb7443daeb12128084f17e1ac80
85e59563f059ecf45c104bef1eac9d70c22150ed
e207411bdb392a2d99719c221c8dd59e0dbebe26
df61cb22643321198656bfa7061c4e415eefdfef
3611ed35610793e814c8aa25715aa582ec08a8b6
089dfe7ceb9a0845342a9637527de65245ba297f qwerty
17570cc9387755367db0fc1c5c5f4757db7fd9b3 asdfgh
82a1be2b77e949cb45581c4d25bf962f77041846 uiop
0b726925f60c17795d4655f8ee37d51a3de70b87 lkjjh
7ed66867332bf06486117189701278cdabd31da6 zxcv
"@

I want to split it into an array of arrays such that the output looks like this:

[0] => [ "089dfe7ceb9a0845342a9637527de65245ba297f", "qwerty" ]
...
[4] => [ "7ed66867332bf06486117189701278cdabd31da6", "zxcv" ]

In other words, an array of 5 elements, with each element being a 2-element array. Note that lines missing the second part (i.e. nothing after the hash) must be excluded.

So far I've got this:

$bar = $foo -split "`r?`n" |?{ $_.length -gt 40 } |%{ $_.split(" ") }

But that results in a single-dimensional array of 10 elements.

Playing around a bit more yields:

$bar = $foo -split "`r?`n" |?{ $_.length -gt 40 } |select { $_.split(" ") }

which gives me an array of 5, but the items in that array are PSCustomObjects with a NoteProperty called $_.split(" ") that is itself an array of 2 strings. So close, yet so far - if I could just flatten out the NoteProperty I'd be in business!

Is it possible to achieve what I desire, bearing in mind these constraints:

  • everything must be done via pipelining
  • no explicitly naming hashtable elements, et cetera - I want

If this is impossible, or there is something I'm fundamentally misunderstanding about how Powershell and its pipelines work (which I very strongly suspect to be the case), please educate me!

2 Answers 2

5

If you want to put an array in the pipeline and prevent PowerShell to drop them as separate pipeline items, you will need to force it to a .Net array list by putting a comma in front of the array object:

PS C:\> $bar = $foo -split "`r?`n" | %{ ,$_.split(" ", 2) }
PS C:\> $bar[7]
82a1be2b77e949cb45581c4d25bf962f77041846
uiop
PS C:\> $bar[7][1]
uiop
Sign up to request clarification or add additional context in comments.

5 Comments

That doesn't drop the empty second elements $bar =$foo -split '\r?\n'| ? {$_ -match '([0-9a-f]{40}) ([^ ].*)'}|%{ ,$_.split(" ") }
it will also create a jagged array if the are spaces in the filename
@LotPings, for the first comment; the second elements are indeed not dropped if they do not exists (unless there is a tailing space) but that shouldn't be an issue as e.g. $bar[1][1] returns $Null (without error) which in most cases automatically typecasts with other strings. If not, you could force it to a string by: "$($Bar[1][1])". For the second comment, you can use the second overload parameter of the .Net Split method to limit the number of strings to 2 (updated answer) which will leave the spaces in the second element: $bar[8][1] now returns in your example: lkjjh test mess
Nicely done; to drop the single-field lines, you can insert -match ' ' after -split "`r?`n"
I love PS but these little trip-ups are so frustrating! Y'all are awesome, especially @iRon.
1

This is a bit clunky, but I think it meets your requirements:

$foo = @"
cfa7b63c88ed1eb7443daeb12128084f17e1ac80
85e59563f059ecf45c104bef1eac9d70c22150ed
e207411bdb392a2d99719c221c8dd59e0dbebe26
df61cb22643321198656bfa7061c4e415eefdfef
3611ed35610793e814c8aa25715aa582ec08a8b6
089dfe7ceb9a0845342a9637527de65245ba297f qwerty
17570cc9387755367db0fc1c5c5f4757db7fd9b3 asdfgh
82a1be2b77e949cb45581c4d25bf962f77041846 uiop
0b726925f60c17795d4655f8ee37d51a3de70b87 lkjjh
7ed66867332bf06486117189701278cdabd31da6 zxcv
"@

$foo | ForEach-Object {$array = New-Object System.Collections.ArrayList}{
    $match = [Regex]::Match($foo, "(?<code1>\w+) (?<code2>\w+)")

    while($match.Success)
    {
        $array.Add(@($match.Groups["code1"].Value, $match.Groups["code2"].Value)) | Out-Null

        $match = $match.NextMatch()
    }
}{$array}

The output is an array of arrays (each with a matching line from $foo), and you can tag a pipeline onto the last bracket and continue processing.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.