How to change values in text files? - regex

I am simply trying to create a powershell script that will change number values in a set of text files. The data in the text files are separated by semi-colons. The values I want to change are always the 2nd and 3rd tokens on each line of the text file.
An example of a line in one of the files:
"Bridge_Asphalt_F";202498.396728;1104.362183;9.721280;0.000000;0.000000;1.000000;-1.299559;
I want to allow the user of the script to enter values to be added to(or subtracted from) the 2nd and 3rd values in all the lines of all the text files in the current directory.
I have a very basic understanding of scripting, but I've been searching around for hours trying to wrap my head around how this would be accomplished.
This is what I have so far but I'm sure I'm getting a few things wrong:
$east = Read-Host 'Easting?'
$north = Read-Host 'Northing?'
Get-ChildItem *.txt |
Foreach-Object {
$c = ($_ | Get-Content)
$c = $c -replace $regexB,$regexB+$east
$c = $c -replace $regexC,$regexC+$north
[IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}
The values determine an object's location on a map (for a game) and I want to be able to move all objects on the entire map by a certain distance on both x and y axis.

Assuming that each line in the file has the same format as your example, then you can treat the file as a CSV and update it like this:
$offset2 = 100
$offset3 = 100
Import-Csv .\data.txt -Delimiter ';' -Header (1 .. 9) |
ForEach-Object {
$_.2 = ([double]$_.2) + $offset2
$_.3 = ([double]$_.3) + $offset3
$_
} | ConvertTo-Csv -NoTypeInformation -Delimiter ';' |
Select-Object -Skip 1 |
Add-Content .\updated.txt
Note:
ConvertTo-Csv surrounds each item with quotes, so you end up with something like this:
"Bridge_Asphalt_F";"202198.396728";"1104.362183";"9.721280";"0.000000";"0.000000";"1.000000";"-1.299559"
This may cause problems if this isn't expected by your game. If so, then some more processing on the pipeline could be done to strip it out.
Also, I've had issues in the past with trying to import and export to the same CSV file, hence my code outputs to a different file. Test it yourself and if it works with the same file, great, otherwise, copy my example, then add a line to replace the existing file with the new one (e.g. using Move-Item).

I guess that's what you need:
cls
cd C:\Users\dandraka\Desktop\test #or whereever
$eastStr = Read-Host 'Easting?'
$northStr = Read-Host 'Northing?'
# convert input to number
$east = [decimal]::Parse($eastStr)
$north = [decimal]::Parse($northStr)
# loop through files
$files = Get-ChildItem *.txt
$files | Foreach-Object {
$fileName = $_.FullName # just for clarity
Write-Host $fileName
$newLines = New-Object System.Collections.ArrayList
# loop through lines of each file
$lines = Get-Content -Path $fileName
$lines | ForEach-Object {
$line = $_.ToString() # just for clarity
$lineItems = $line -split ';'
$pointName = $lineItems[0]
$latitudeStr = $lineItems[1]
$longitudeStr = $lineItems[2]
# convert to number
$latitude = [decimal]::Parse($latitudeStr)
$longitude = [decimal]::Parse($longitudeStr)
Write-Host "$pointName latitude $latitude , longitude $longitude"
# do the math
$newLatitude = $latitude + $north
$newLongitude = $longitude + $east
Write-Host "$pointName new latitude $newLatitude , new longitude $newLongitude"
# recontruct the line
$newLine = ""
for($i=0; $i -lt $lineItems.Count; $i++) {
if ($i -eq 1) {
$newLine += "$newLatitude;"
continue
}
if ($i -eq 2) {
$newLine += "$newLongitude;"
continue
}
# this if fixes a small bug, without it there are two ; at the end of each line
if ($lineItems[$i].Length -gt 0) {
$newLine += "$($lineItems[$i]);"
}
}
Write-Host "Old line $line"
Write-Host "New line $newLine"
$newLines.Add($newLine) | Out-Null
}
# write file
$newFilename = $fileName.Replace(".txt", ".dat")
[System.IO.File]::WriteAllLines($newFilename, $newLines)
Write-Host "File $newFilename written"
}
A few things to note here:
As you mention that you're starting with powershell, I've written the code more verbose than I would for, say, a seasoned developer. But that actually doesn't hurt.
For the same reason, the code is sub-optimal on purpose (makes for easier to read code). But for better performance and large files (say, a few 10s of MB or more) you need to do things differently, e.g. avoid strings and use string builder instead.
Obviously you can comment out all the Write-Host statements, they're there just to help you make sure the code is working properly.
Hope that helps!
Jim

If your game cannot handle the quoted coordinate values you get when using ConvertTo-Csv or Export-Csv, this should update the values while leaving the quotes off:
$eastOffset = 100
$northOffset = -200
(Get-Content 'D:\coordinates.txt') | ForEach-Object {
$fields = $_ -split ';'
[double]$fields[1] += $eastOffset
[double]$fields[2] += $northOffset
# write the updated stuff to file
Add-Content -Path 'D:\newcoordinates.txt' -Value ($fields -join ';')
}
this content
"Bridge_Asphalt_F";202498.396728;1104.362183;9.721280;0.000000;0.000000;1.000000;-1.299559;
"Road_Asphalt_F";202123.396728;1104.362456;9.721280;0.000000;0.000000;1.000000;-1.299559;
would become
"Bridge_Asphalt_F";202598.396728;904.362183;9.721280;0.000000;0.000000;1.000000;-1.299559;
"Road_Asphalt_F";202223.396728;904.362456;9.721280;0.000000;0.000000;1.000000;-1.299559;

Related

Powershell file property listing

I'm trying to collect some file properties using PowerShell within Win 2008. To do so, I've created the following script.
# BASIC PARAMETER IF NOT SET
param(
$REGEX='.*'
)
# CURRENT DATE FROM 00H
$DATAATUAL = Get-Date -Hour 0 -Minute 0 -Second 0 -Day 1 # DAY 1 FOR TESTING ONLY
# APPLICATION'S FILE PATH
$PATH = "C:\FTP\import\"
# FILE LIST WITH SELECTED FILES FROM REGULAR EXPRESSION
$FILELIST = Get-ChildItem -Path $PATH | Where-Object { ($_.LastWriteTime -ge $DATAATUAL) -and ($_.Name -cmatch "$REGEX") } | Select-Object -ExpandProperty Name
# OUTPUT IN A SORT OF CSV FORMAT
if ($FILELIST -ne $null) {
Write-Host "name;suffix;fileprocstart;filesize;filewrite"
ForEach ($FILE in $FILELIST) {
# FILE NAME PREFFIX AND SUFFIX
$FILENAME = Select-String -InputObject $FILE -CaseSensitive -Pattern "(^\d+)_($REGEX)"
# FILE TIMESTAMP CONVERTION TO EPOCH UTC-0
$FILEPROCSTART = $FILENAME.Matches.Groups[1].value
$FILEPROCSTART = [datetime]::ParseExact($FILEPROCSTART,"yyyyMMddHHmmss",$null) | Get-Date -UFormat "%s"
$FILEPROCSTART = $($FILEPROCSTART -as [long]) + 10800 # TIMEZONE CORRECTION - ADDED 3H TO BECOME UTC-0
$FILESUFFIX = $FILENAME.Matches.Groups[2].value
# FILE SIZE AND WRITE TIME
$FILESIZE = Get-ChildItem -Path $PATH -Filter $FILE | Select-Object -ExpandProperty Length
$FILEWRITE = Get-ChildItem -Path $PATH -Filter $FILE | Select-Object -ExpandProperty LastWriteTime | Get-Date -UFormat "%s"
# OUTPUT
Write-Host "$FILENAME;$FILESUFFIX;$FILEPROCSTART;$FILESIZE;$FILEWRITE"
}
}
# NO FILES FOUND
Else {
Write-Host "Empty"
}
I can start it like so:
script.ps1 -REGEX 'pattern'
It results in a list like this:
name;suffix;fileprocstart;filesize;filewrite
20220709101112_cacs1_v83.txt;cacs1_v83.txt;1657361472;5;1657397022,47321
20220709101112_cacs1_v83.txt.log;cacs1_v83.txt.log;1657361472;5;1657397041,83271
20220709101112_cacs2_v83.txt;cacs2_v83.txt;1657361472;5;1657397039,70775
20220709101112_cacs3_v83.txt.log;cacs3_v83.txt.log;1657361472;5;1657397038,03647
20220709101112_cakauto4.txt;cakauto4.txt;1657361472;5;1657397037,48906
20220709111112_coord_multicanal.txt.log;coord_multicanal.txt.log;1657365072;5;1657398468,95865
All files are generated on a daily basis and have a format similar to this:
20220709101112_cacs1_v83.txt
20220709101112_cacs1_v83.txt.log
20220709101112_cacs2_v83.txt
20220709101112_cacs3_v83.txt.log
20220709101112_cakauto4.txt
20220709101112_coord_multicanal.txt.log
Basically, the script outputs the file name, file suffix (no timestamp), file timestamp (unix format), file size and Last Write time (unix format), all in a sort of CSV format. It is meant to be started by another system to collect those properties.
It kind of works, but I can't help thinking there must be a better way to do that.
Any considerations on how to improve it?
I'm not sure if I got it right but if I understand this right:
Basically, the script outputs the file name, file suffix, file name timestamp, file size and Last Write time, all in a sort of CSV format. It is meant to be started by another system to collect those properties.
This should be all you need to start with:
$ComputerName = 'RemoteW2K8Computer'
Invoke-Command -ComputerName $ComputerName -ScriptBlock {
Get-ChildItem -Path 'C:\FTP\import' |
Select-Object -Property BaseName,Extension,Length,LastWriteTime,
#{Name = 'FileNameTimeStamp'; Expression = {($_.BaseName -split '_')[0]}}
}
Using #Olaf great tips, I've rewritten the script this way.
param($REGEX='.*')
$DATAATUAL = Get-Date -Hour 0 -Minute 0 -Second 0 -Day 1 # DAY 1 FOR TESTING ONLY
$PATH = "C:\FTP\import"
$TZ = [TimeZoneInfo]::FindSystemTimeZoneById("E. South America Standard Time")
$FILELIST = Get-ChildItem -Path $PATH |
Where-Object { ($_.LastWriteTime -ge $DATAATUAL) -and ($_.Name -cmatch "$REGEX") } |
Select-Object -Property Name,Length,
#{Name = 'Suffix'; Expression = { ($_.Name -split '_',2)[1] } },
#{Name = 'ProcStart'; Expression = {
$PROCSTART = ($_.Name -split '_')[0];
$PROCSTART = [datetime]::ParseExact($PROCSTART,"yyyyMMddHHmmss",$null);
[TimeZoneInfo]::ConvertTimeToUtc($PROCSTART, $TZ) | Get-Date -UFormat "%s";
} },
#{Name = 'FileWrite' ; Expression = {
$WRITETIME = $_.LastWriteTime;
[TimeZoneInfo]::ConvertTimeToUtc($WRITETIME) | Get-Date -UFormat "%s";
} }
if ($FILELIST -ne $null) {
Write-Host "name;suffix;procstart;filesize;filewrite"
# $FILELIST | ConvertTo-Csv -Delimiter ';' -NoTypeInformation
ForEach ($FILE in $FILELIST) {
$FILENAME = $FILE.Name
$FILESUFFIX = $FILE.Suffix
$FILESIZE = $FILE.Length
$FILEPROCSTART = $FILE.ProcStart
$FILEWRITE = $FILE.FileWrite
Write-Host "$FILENAME;$FILESUFFIX;$FILESIZE;$FILEPROCSTART;$FILEWRITE"
}
}
Else {
Write-Host "Empty"
}
As said, the output is in a CSV format.
name;suffix;procstart;filesize;filewrite
20220709101112_cacs1_v83.txt;cacs1_v83.txt;5;1657361472;1657397022,47321
20220709101112_cacs1_v83.txt.log;cacs1_v83.txt.log;5;1657361472;1657397041,83271
If I use ConvertTo-Csv (much simpler) instead of ForEach, the output would also be a CSV.
However, it places quotation marks that mess up other conversions to JSON elsewhere (maybe I can improve that later).
# $FILELIST | ConvertTo-Csv -Delimiter ';' -NoTypeInformation
"Name";"Length";"Suffix";"ProcStart";"FileWrite"
"20220709101112_cacs1_v83.txt";"5";"cacs1_v83.txt";"1657361472";"1657397022,47321"
"20220709101112_cacs1_v83.txt.log";"5";"cacs1_v83.txt.log";"1657361472";"1657397041,83271"
The other system convert it to this (I can't use ConvertTo-Json in Win2008 :-/):
{
"\"Name\"": "\"20220709101112_cacs1_v83.txt\"",
"\"Length\"": "\"5\"",
"\"Suffix\"": "\"cacs1_v83.txt\"",
"\"ProcStart\"": "\"1657361472\"",
"\"FileWrite\"": "\"1657397022,47321\""
}
Therefore, I find that writing the values with ForEach gives me a cleaner output.
Also, for fun, measuring with Measure-Command, I found that the new script is a bit faster.
The previous script takes about 24 milliseconds to complete while using a small dataset.
Now, the new one takes about 13 milliseconds with the same dataset.
All in all, a small, but good improvement, I guess.
Cheers to #Olaf for pointing to a better script and for his patience. Thank you.

PowerShell to match multiple lines with regex pattern

I write a Powershell script and regex to search two configs text files to find matches for Management Vlan. For example, each text file has two Management vlan configured as below:
Config1.txt
123 MGMT_123_VLAN
234 MGMT_VLAN_234
Config2.txt
890 MGMT_VLAN_890
125 MGMT_VLAN_USERS
Below is my script. It has several problems.
First, if I ran the script with the $Mgmt_vlan = Select-String -Path $File -Pattern $String -AllMatches then the screen output shows the expected four (4) Mgmt vlan, but in the CSV file output shows as follow
Filename Mgmt_vlan
Config1.txt System.Object[]
Config2.txt System.Object[]
I ran the script the output on the console screen shows exactly four (4) Management vlans that I expected, but in the CSV file it did not. It shows only these vlans
Second, if I ran the script with $Mgmt_vlan = Select-String -Path $File -Pattern $String | Select -First 1
Then the CSV shows as follows:
Filename Mgmt_vlan
Config1.txt 123 MGMT_123_VLAN
Config2.txt 890 MGMT_VLAN_890
The second method Select -First 1 appears to select only the first match in the file. I tried to change it to Select -First 2 and then CSV shows column Mgmt_Vlan as System.Object[].
The result output to the screen shows exactly four(4) Mgmt Vlans as expected.
$folder = "c:\config_folder"
$files = Get-childitem $folder\*.txt
Function find_management_vlan($Text)
{
$Vlan = #()
foreach($file in files) {
Mgmt_Vlan = Select-String -Path $File -Pattern $Text -AllMatches
if($Mgmt_Vlan) # if there is a match
{
$Vlan += New-Object -PSObject -Property #{'Filename' = $File; 'Mgmt_vlan' = $Mgmt_vlan}
$Vlan | Select 'Filename', 'Mgmt_vlan' | export-csv C:\documents\Mgmt_vlan.csv
$Mgmt_Vlan # test to see if it shows correct matches on screen and yes it did
}
else
{
$Vlan += New-Object -PSObject -Property #{'Filename' = $File; 'Mgmt_vlan' = "Mgmt Vlan Not Found"}
$Vlan | Select 'Filename', 'Mgmt_vlan' | Export-CSV C:\Documents\Mgmt_vlan.csv
}
}
}
find_management_vlan "^\d{1,3}\s.MGMT_"
Regex correction
First of all, there are a lot of mistakes in this code.
So this is probably not code that you actually used.
Secondly, that pattern will not match your strings, because if you use "^\d{1,3}\s.MGMT_" you will match 1-3 numbers, any whitespace character (equal to [\r\n\t\f\v ]), any character (except for line terminators) and MGMT_ chars and anything after that. So not really what you want. So in your case you can use for example this: ^\d{1,3}\sMGMT_ or with \s+ for more than one match.
Code Correction
Now back to your code... You create array $Vlan, that's ok.
After that, you tried to get all strings (in your case 2 strings from every file in your directory) and you create PSObject with two complex objects. One is FileInfo from System.IO and second one is an array of strings (String[]) from System. Inside the Export-Csv function .ToString() is called on every property of the object being processed. If you call .ToString() on an array (i.e. Mgmt_vlan) you will get "System.Object[]", as per default implementation. So you must have a collection of "flat" objects if you want to make a csv from it.
Second big mistake is creating a function with more than one responsibility. In your case your function is responsible for gathering data and after that for exporting data. That's a big no no. So repair your code and move that Export somewhere else. You can use for example something like this (i used get-content, because I like it more, but you can use whatever you want to get your string collection.
function Get-ManagementVlans($pattern, $files)
{
$Vlans = #()
foreach ($file in $files)
{
$matches = (Get-Content $file.FullName -Encoding UTF8).Where({$_ -imatch $pattern})
if ($matches)
{
$Vlans += $matches | % { New-Object -TypeName PSObject -Property #{'Filename' = $File; 'Mgmt_vlan' = $_.Trim()} }
}
else
{
$Vlans += New-Object -TypeName PSObject -Property #{'Filename' = $File; 'Mgmt_vlan' = "Mgmt Vlan Not Found"}
}
}
return $Vlans
}
function Export-ManagementVlans($path, $data)
{
#do something...
$data | Select Filename,Mgmt_vlan | Export-Csv "$path\Mgmt_vlan.csv" -Encoding UTF8 -NoTypeInformation
}
$folder = "C:\temp\soHelp"
$files = dir "$folder\*.txt"
$Vlans = Get-ManagementVlans -pattern "^\d{1,3}\sMGMT_" -files $files
$Vlans
Export-ManagementVlans -path $folder -data $Vlans```
Summary
But in my opinion in this case is overprogramming to create something like you did. You can easily do it in oneliner (but you didn't have information if the file doesn't include anything). The power of powershell is this:
$pattern = "^\d{1,3}\s+MGMT_"
$path = "C:\temp\soHelp\"
dir $path -Filter *.txt -File | Get-Content -Encoding UTF8 | ? {$_ -imatch $pattern} | select #{l="FileName";e={$_.PSChildName}},#{l="Mgmt_vlan";e={$_}} | Export-Csv -Path "$path\Report.csv" -Encoding UTF8 -NoTypeInformation
or with Select-String:
dir $path -Filter *.txt -File | Select-String -Pattern $pattern -AllMatches | select FileName,#{l="Mgmt_vlan";e={$_.Line}} | Export-Csv -Path "$path\Report.csv" -Encoding UTF8 -NoTypeInformation

CSV only contains last entry from file

The CSV file only contains a partial entry from the last regex match.
I've used the ISE debugger and can verify it's finding matches.
$h = #{}
$a = #()
Get-ChildItem C:\Users\speterson\Documents\script\*.kiy | foreach {
Get-Content $_ | foreach {
if ($_ -match 'IF Ingroup\s+\(\s+\"(..+?)\"\s+\)') {
$h.Group = $matches[1]
}
if ($_ -match 'use\s+([A-Za-z]):"(\\\\..*?\\..*)\"')) {
$h.DriveLetter = $matches[1].ToUpper()
$h.Path = $matches[2]
}
}
$a += New-Object PSCustomObject -Property $h
}
$a | Export-Csv c:\temp\Whatever.csv -NoTypeInfo
The input files look like this, but have 1000+ lines in them:
IF Ingroup ( "RPC3WIA01NT" )
use v: /del
ENDIF
IF Ingroup ( "JWA03KRONOSGLOBAL" )
use v:"\\$homesrvr\$dept"
ENDIF
IF Ingroup ( "P-USERS" )
use p:'\\PServer\PDRIVE
ENDIF
CSV file only shows:
GROUP
P-USERS
I want to ignore the drive letters with the /del.
I'm trying to get a CSV file that shows
Group Drive Path
JWA03KRONOSGLOBAL V \\$homesrvr\$dept
P-USERS P \\PServer\PDRIVE
Your code has two loops, one nested in the other. The outer loop processes each file from the Get-ChildItem call. The inner loop processes the content of the current file of the outer loop. However, since you're creating your objects after the inner loop finished you're only getting the last result from each processed file. Move object creation into the inner loop to get all results from all files.
I'd also recommend not re-using a hashtable. Re-using objects always bears the risk of having data carried over somewhere undesired. Hashtable creation is so inexpensive that running that risk is never worth it.
On top of that your processing of the files' content is flawed, because the inner loop processes the content one line at a time, but both of your conditionals match on different lines and are not linked to each other. If you created a new object with every iteration that would give you incorrect results. Read the file as a whole and then use Select-String with a multiline regex to extract the desired information.
Another thing to avoid is appending to an array in a loop (that's a slow operation because it involves re-creating the array and copying elements over and over). Since you're using ForEach-Object you can pipe directly into Export-Csv.
Something like this should work:
$re = 'IF Ingroup\s+\(\s+"(.+?)"\s+\)\s+' +
"use\s+([a-z]):\s*[`"'](\\\\[^`"'\s]+)"
Get-ChildItem 'C:\Users\speterson\Documents\script\*.kiy' | ForEach-Object {
Get-Content $_.FullName |
Out-String |
Select-String $re -AllMatches |
Select-Object -Expand Matches |
ForEach-Object {
New-Object -Type PSObject -Property #{
'Group' = $_.Groups[1].Value
'DriveLetter' = $_.Groups[2].Value
'Path' = $_.Groups[3].Value
}
}
} | Export-Csv 'C:\path\to\output.csv' -NoType

How to replace a text if it was interrupted by a line break or a tag?

I read a file from a program with powershell and want to replace certain strings there. For example, the content of a file looks like this:
<"#319",
#"Sprache" = "DE">
<F5#T#Z7#L1031>5<F5#T#Z7#L1033>)<F5#Z7#L1031><Tab>#319-10002
Now I want to replace e.g. #319-10002 by Some Text, which is no problem.
$output = New-Object System.IO.StreamWriter (Join-Path -Path $outputPath -ChildPath "$outputName.ildoc")
Get-Content -Path (Join-Path -Path $inputPath -ChildPath "$inputName.ildoc") -ReadCount 512 | ForEach-Object {
$value = $_
foreach($entry in $csvInput.GetEnumerator()) {
$value = $value -replace $entry.Name, $entry.Value
}
$output.Write((($value | Out-String) -replace "`r`n", "`n"))
}
$output.Close()
Those "variables" are from a CSV file and are therefore dynamic. There are 2 types:
#Number-Number
#Number-Text
Now there is also the case that the line is longer than 80 characters. Then the program that generated the file automatically inserts a line break after 80 characters.
<"#319",
#"Sprache" = "DE">
<F5#T#Z1#L1031>3<F5#T#Z1#L1033>)<F5#Z1#L127><Tab><F5#Z1#L1031>Some more Text #3
19-10002
Then there is also the case that the string is interrupted by tags. If text is in tables and the text is longer than the column, the program automatically inserts a <SR> followed by a line break at the place of the break.
<Cell, Straddle = 2,
Top Ruling Color = 16,
Left Ruling Weight = 0.75,
Left Ruling Color = 30,
Left Ruling Visible = no><!Page,
Left Margin = 0.0039372 Inches,
Right Margin = 0.0039372 Inches>
<"text:zentr">
#319-10<SR>
002
I am currently quite at a loss how to solve the problem. Do you have an idea how to solve this?
Before applying your replacements you could preprocess the file
to remove the Pos~80 and <SR> breaks with Regular Expressions:
(Get-Content .\input.ildoc -raw) -replace '(?m)(?<=^.{79,})\r?\n' -replace '\<SR\>\r?\n'
yiedls here (with both above samples)
<"#319",
#"Sprache" = "DE">
<F5#T#Z1#L1031>3<F5#T#Z1#L1033>)<F5#Z1#L127><Tab><F5#Z1#L1031>Some more Text #319-10002
<Cell, Straddle = 2,
Top Ruling Color = 16,
Left Ruling Weight = 0.75,
Left Ruling Color = 30,
Left Ruling Visible = no><!Page,
Left Margin = 0.0039372 Inches,
Right Margin = 0.0039372 Inches>
<"text:zentr">
#319-10002
I think you need to tackle this using some cleaning up regexes on the file content first.
Perhaps this could help:
First pass to deal with #Number-Number or #Number-Text broken by line breaks
$content = $content -replace '(#)(?:\r?\n)*(\d+)(?:\r?\n)*([-\w]+)', '$1$2$3'
this changes
<F5#T#Z1#L1031>3<F5#T#Z1#L1033>)<F5#Z1#L127><Tab><F5#Z1#L1031>Some more Text #3
19-10002
<F5#T#Z1#L1031>3<F5#T#Z1#L1033>)<F5#Z1#L127><Tab><F5#Z1#L1031>Some more Text #3
20-TEXT
into
<F5#T#Z1#L1031>3<F5#T#Z1#L1033>)<F5#Z1#L127><Tab><F5#Z1#L1031>Some more Text #319-10002
<F5#T#Z1#L1031>3<F5#T#Z1#L1033>)<F5#Z1#L127><Tab><F5#Z1#L1031>Some more Text #320-TEXT
Second pass deals with <SR> followed by newlines:
$content = $content -replace '(#[-\d]+)<SR>\r?\n([-\w]+)', '$1$2'
this changes stuff like
#319-10<SR>
002
into
#319-10002
After this, the elements to replace should be no longer broken up.
Putting this together, you can try this code:
# get the content of the file as a single string including line breaks
$content = Get-Content -Path (Join-Path -Path $inputPath -ChildPath "$inputName.ildoc") -Raw
# remove garbage linebreaks
$content = $content -replace '(#)(?:\r?\n)*(\d+)(?:\r?\n)*([-\w]+)', '$1$2$3'
# remove garbage <SR>linebreaks
$content = $content -replace '(#[-\d]+)<SR>\r?\n([-\w]+)', '$1$2'
# split the content into an array of strings and loop through
for ($i = 0; $i -lt ($content -split '\r?\n').Count; $i++) {
$line = $content[$i]
foreach($entry in $csvInput.GetEnumerator()) {
$line = $line -replace $entry.Name, $entry.Value
}
# overwrite the string element with the replaced value
$content[$i] = $line
}
# finally write the updated array as string joined with '\n'
Set-Content -Path (Join-Path -Path $outputPath -ChildPath "$outputName.ildoc") -Value ($content -join '\n')

Powershell Lines of code count

Im having some trouble counting the lines of code in all my powershell projects.
I want to ignore comment sections in my count, unfortunately I am not that good with regular expressions.
So what I want to achieve is to exclude all the "Synopsis help code in functions"
<#
.SYNOPSIS
#>
And my own comment blocks
<#
Get-ADUser -Identity ThisUserDoesNotExist
ThisCodeIsCommentedOut
#>
What I have so far is
Get-Content Script.ps1 | ?{$_ -ne "" -and $_ -notlike "#*"}
If you are in v3.0 I suggest to use this script: http://poshcode.org/4789
Here the relevant part modified just to count lines of code of a script file:
$file = ".\My_Script_File.ps1"
$fileContentsArray = Get-Content -Path $file
if ($fileContentsArray)
{
$codeLines = $null
$tokenAst = $null
$parseErrorsAst = $null
# Use the PowerShell 3 file parser to create the scriptblock AST, tokens and error collections
$scriptBlockAst = [System.Management.Automation.Language.Parser]::ParseFile($file, [ref]$tokenAst, [ref]$parseErrorsAst)
# Calculate the 'lines of code': any line not containing comment or commentblock and not an empty or whitespace line.
# Remove comment tokens from the tokenAst, remove all double newlines and count all the newlines (minus 1)
$prevTokenIsNewline = $false
$codeLines = #($tokenAst | select -ExpandProperty Kind | where { $_ -ne "comment" } | where {
if ($_ -ne "NewLine" -or (!$prevTokenIsNewline))
{
$_
}
$prevTokenIsNewline = ($_ -eq "NewLine")
} | where { $_ -eq "NewLine" }).Length-1
$codeLines
}