0

I am working on a script that collects a lot of lines of data. The main thing bugging me is I want the Name to be copied after a string until it hits the next Name item and from then use that name to the next strings. I will turn this text file into a CSV and the rest of my script takes over from that. But I cannot do that until I can correctly comma deliniate this. The format I want is:

X,Y,Name
Lat,Lon,Red1
Lat,Lon,Red1
Lat,Lon,Red2
Lat,Lon,Red3
...

Currently I have my source text organized as:

Red1 
Lat,Lon
Lat,Lon
Red2
Lat,Lon
Red3
Lat,Lon
...

Due to my source text will change and usually involves more than 500 lines, I do not want to simply copy and paste it in CSV format, that is making me prompt the user to make the changes and then hit yes to proceed, I wish to make the script a click once and out comes the result.

I have tried to use something like this:

(Get-Content $Makron2\kmlPrep1.txt -Raw) -replace '<kml:name>(.*?)</kml:name>', '<kml:name>,,NameStart,$1</kml:name>' |Set-Variable -Name "NameX" -Value '$1' |Set-Content -path $Makron2\kmlPrep1.txt

I want what is in between the <kml:name> </kml:name>, save it as a variable, and then paste it after Lat,Lon,$NameX

(Get-Content $Makron2\kmlPrep2.txt) -replace '<kml:coordinates>','$NameX' | Set-Content -Path $Makron2\kmlChangeMe.txt

In the previous I have it replacing as well for another purpose, but I am wondering if its possible to save what is in the (.*?) and then recall it later rather just in this statement. the previous only returns $1 instead of Red1 or any other names.

But it's not working for me, as I am limited in how to grep things in PowerShell. I am mostly self taught in this.

Destroy666
  • 892
  • 12
  • 19
Rubric23
  • 11
  • 3
  • 1
    It's still quite unclear after self-formatting it. Please show example of output(s) that you need. – Destroy666 May 12 '23 at 07:42
  • I need for the output, to copy the Name is(Red) and place it after the Lat/Lon with a comma and the Name. – Rubric23 May 12 '23 at 09:26
  • So, basically, what you are saying is that if a line contains no comma, it's a new name. And if the line does contain a comma, it's a lat, lon pair and is to be used with the most recent name to generate a triplet for the output csv. Is this correct? – Walter Mitty May 12 '23 at 10:10
  • 1
    @Rubric23 ok, that part is more or less clear. But why do you mention e.g. ``? That's nowhere to be found in the shown input/output. – Destroy666 May 12 '23 at 14:35

3 Answers3

0

Try using double quotes instead of single quotes surrounding the $1. Single quotes prevent the $1 from being evaluated, and instead it is treated as literal text.

Better yet, replace the entire Set-Value phrase with

$Name = $1

This is the usual way to copy a value from one variable to another.

Walter Mitty
  • 18,205
  • 2
  • 28
  • 58
0

You do not need Regex. Just test if line has a comma

$data = @"
Red1

Lat,Lon

Lat,Lon

Red2

Lat,Lon

Red3

Lat,Lon
"@

$reader = [System.IO.StringReader]::new($data);
$table = [System.Collections.ArrayList]::new();
$location = '';

while(($line =$reader.ReadLine()) -ne $null)
{
   $line = $line.Trim();

   if($line.Length -gt 0)
   {
      if(-not $line.Contains(','))
      {
         $location = $line;
      }
      if($line.Contains(','))
      {
         $splitLine = $line.Split(',');
         $newRow = New-Object -TypeName psobject;
         $newRow | Add-Member -NotePropertyName Location -NotePropertyValue $location; 
         $newRow | Add-Member -NotePropertyName X -NotePropertyValue $splitline[0].Trim();
         $newRow | Add-Member -NotePropertyName Y -NotePropertyValue $splitline[1].Trim();
         $table.Add($newRow)  | Out-Null;
      }
   }
}
$table;

Results

Location X   Y
-------- -   -
Red1     Lat Lon
Red1     Lat Lon
Red2     Lat Lon
Red3     Lat Lon
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • While the requirements are a little fuzzy, the OP's own attempt shows an attempt to extract the value of an XML element using a regex, so looking just for `,` is not enough. As previously discussed [here](https://stackoverflow.com/a/75348106/45375) and [here](https://stackoverflow.com/a/76011282/45375), I am down-voting for unidiomatic PowerShell code that sets a bad example. – mklement0 May 12 '23 at 15:58
  • @mklement0 : The requirements are perfectly clear except for the fact some location have multiple LAT/LON. The lines without the comma are the names of the location and the ones with commas are the ones with Latitude and Longitude values. – jdweng May 12 '23 at 16:15
0

The exact requirements aren't clear, because your sample input doesn't match your solution attempt, but the problem seems to come down to this:

  • When processing lines, you need to maintain state, namely by caching the most recent name, which is part of the value from a single-value line, in a variable.

  • That variable can then be used in modifying the subsequent multi-value lines.

The switch statement enables both stateful line-by-line processing as well as regex-matching of lines:

$( # Wrap the switch statement in $(...), so it can be used in a pipeline.

  switch -Regex -File $Makron2\kmlPrep2.txt {
    # A (new) name line -> store the relevant part in $name
    '<kml:name>(.*?)</kml:name>' { $name = $Matches.1 }
    # A multi-value line: use $name to augment it and implicitly output the result.
    default                      { "$_,$name" }
  }

) # | Set-Content ...

Note the use of the automatic $Matches variable variable to refer to the results of the regex-matching operation (this variable is also populated when you use the -match operator).

Enclosing the switch statement in $(...), the subexpression operator allows it to participate in a pipeline.

  • $(...) collects all output in memory first, which is the prerequisite for writing back results to the input file.

  • If that is not needed and memory-friendly streaming processing is desired, use & { ... } instead, i.e. enclose the switch statement in a script block ({ ... }) and invoke with &, the call operator.


As for what you tried:

Fundamentally, your approach lacks stateful cross-line processing, but to address some aspects that are inherently broken:

  • The $1 placeholder is only meaningful inside the substitution operand of a -replace operation, where it refers to the value of the 1st capture group in the regex.

    • Despite its appearance, it is not a PowerShell variable, and is unrelated to a PowerShell variable of that name.
  • Therefore, your attempt to use Set-Variable with $1 cannot work; moreover '$1' - due to use of '...' rather than "..." quoting - is a verbatim string literal whose verbatim content is $1 - no string expansion (interpolation) is performed.

  • Set-Variable has no output (except if you add -PassThru), so using it as input to another command in the pipeline is pointless.

    • Set-Variable is rarely used in practice, because $var = 'value' is both more concise and efficient than Set-Variable var 'value' - though $var = 'value' requires an expression context, which inside a pipeline usually means use inside a script block ({ ... }) passed to ForEach-Object or, as shown above, in action blocks of switch statements.
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • This definately helped me! unfortantely, it adds extra lines at the end of the text for whatever reason, might be a result from earlier in the script. But this is the result i was looking for! Many thanks! – Rubric23 May 12 '23 at 15:46
  • @Rubric23, glad to hear it; my pleasure. Not sure about the extra lines; may indeed be something unrelated to this solution. – mklement0 May 12 '23 at 15:48