Powershell, replace string line by line in a textfile - powershell

I have a file called "file123624.TXT" that contains this information:
FKHOGU1100;;;;;;;;;;;;;randomdata;1;0;2;1234
YJKMRI1101;;;;;;;;;;;;;randomdata;1;0;2;1234
FWPCYY1113;GV;randomdata;5;;;;;;;6018;randomdata;GU;;1;0;2;1234
VOBYTM1100;;;;;;;;;;;;;randomdata;1;0;2;1234
ZSOKHW1160;GV;randomdata;53;;;;;;;7353;randomdata;GU;;1;0;2;1234
YCHQHS1123;GV;randomdata;4;;;;;;;5063;randomdata;GU;;1;0;2;1234
YXRCZO1105;GV;randomdata;39;;;;;;;9510;randomdata;GU;;1;0;2;1234
XVDUEM1100;GV;randomdata;14;;;;;;;9901;randomdata;GU;;1;0;2;1234
CHECKSUM;0000008
All i want to do is add an - after the first six characters in the file, except for the last line "CHECKSUM; 0000008"
I have made a small powershell script that almost does the trick:
$file = Get-Content "C:\Users\usr\Desktop\file*.txt"
foreach ($i in $file)
{
if($i -notmatch "CHECKSUM*")
{$I.Insert(6,'-')}
}
This script output the lines i need to be changed, but i cant replace them line for line.
The result i want in the "file123624.txt" after running the script is this:
FKHOGU-1100;;;;;;;;;;;;;randomdata;1;0;2;1234
YJKMRI-1101;;;;;;;;;;;;;randomdata;1;0;2;1234
FWPCYY-1113;GV;randomdata;5;;;;;;;6012;randomdata;GU;;1;0;2;1234
VOBYTM-1100;;;;;;;;;;;;;randomdata;1;0;2;1234
ZSOKHW-1160;GV;randomdata;53;;;;;;;7653;randomdata;GU;;1;0;2;1234
YCHQHS-1123;GV;randomdata;4;;;;;;;5463;randomdata;GU;;1;0;2;1234
YXRCZO-1105;GV;randomdata;39;;;;;;;9210;randomdata;GU;;1;0;2;1234
XVDUEM-1100;GV;randomdata;14;;;;;;;9401;randomdata;GU;;1;0;2;1234
CHECKSUM;0000008
Any solutions or tips on this would be appreciated

You can do the following, which utilizes the Foreach-Object and has a similar structure to your current code.
$file = Get-Content file123624.TXT | Foreach-Object {
if ($_ -notmatch '^CHECKSUM') {
$_.Insert(6,'-')
}
else {
$_
}
}
$file | Set-Content file123624.TXT

Related

Changing multiple lines in a text file based on a psobject

I'm working on a script which will add some additional informations to a txt file. These informations are stored in a CSV file which looks like this (the data will differs each time the script will launch):
Number;A;B;ValueOfB
FP01340/05/20;0;1;GTU_01,GTU_03
FP01342/05/20;1;0;GTU01
The txt file looks like this (data inside will of course differ each time):
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere|||||
2|zwol|9,00|9,00|0,00
2|23|157,91|194,23|36,32
1|1|FP01341/05/20|2020-05-02|2020-05-02|2020-05-02|12,19|14,99|2,80|Some info |2222222|blabla|11-111 something||||
2|23|12,19|14,99|2,80
1|1|FP01342/05/20|2020-05-02|2020-05-02|2020-05-02|525,36|589,64|64,28|bla|222222|blba 36||62030|something||
2|5|213,93|224,63|10,70
2|8|120,34|129,97|9,63
2|23|191,09|235,04|43,95
What I need to do is to find a line which contains 'Number' and then add value 'A' and 'B' from a CSV in a form: |0|1 and then on the first line below, at the end, add 'ValueofB' in a form |AAA_01,AAA_03
So the first two lines should look like this at the end:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere||||||0|1
2|zwol|9,00|9,00|0,00|AAA_01,AAA_03
2|23|157,91|194,23|36,32
Rest of lines should not be touched.
I made a script which uses select-string method with context to find what I need to - put that into an object and then add to previously found strings what I need to and put that in to an another object.
My script is as follws:
$csvFile = Import-Csv -Path Somepath\file.csv -Delimiter ";"
$file = "Somepath2\SomeName.txt"
$LinesToChange = #()
$script:LinesToChange = $LinesToChange
$LinesOriginal = #()
$script:LinesOriginal = $LinesOriginal
foreach ($line in $csvFile) {
Select-String -Path $file -Pattern "$($Line.number)" -Encoding default -Context 0, 1 | ForEach-Object {
$1 = $_.Line
$2 = $_.Context.PostContext
}
$ListOrg = [pscustomobject]#{
Line_org = $1
Line_GTU_org = $2
}
$LinesOriginal = $LinesOriginal + $ListOrg
$lineNew = $ListOrg.Line_org | foreach { $_ + "|$($line.A)|$($line.B)" }
$GTUNew = $ListOrg.Line_GTU_org | foreach { $_ + "|$($line.ValueofB)" }
$ListNew = [pscustomobject]#{
Line_new = $lineNew
Line_GTU_new = $GTUNew
Line_org = $ListOrg.Line_org
Line_GTU_org = $ListOrg.Line_GTU_org
}
$LinesToChange = $LinesToChange + $ListNew
}
The output is an object $LinesToChange which have original lines and lines after the change. The issue is I have no idea how to use that to change the txt file. I tried few methods and ended up with file which contains updated lines but all others are doubbled (I tried foreach) or PS is using whole RAM and couldn't finish the job :)
My latest idea is to use something like that:
(Get-Content -Path $file) | ForEach-Object {
$line = $_
$LinesToChange.GetEnumerator() | ForEach-Object {
if ($line -match "$($LinesToChange.Line_org)") {
$line = $line -replace "$($LinesToChange.Line_org)", "$($LinesToChange.Line_new)"
}
if ($line -match "$($LinesToChange.Line_GTU_org)") {
$line = $line -replace "$($LinesToChange.Line_GTU_org)", "$($LinesToChange.Line_GTU_new)"
}
}
} | Set-Content -Path Somehere\newfile.txt
It seemed promising at first, but the variable $line contains all lines and as such it can't find the match.
Also I need to be sure that the second line will be directly below the first one (it is unlikely but it can be a case that there will be two or more lines with the same data while the "number" from CSV file is unique) so preferably while changing the txt file it would be needed to find a match for a two-liner; in short:
find this two lines:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere|||||
2|zwol|9,00|9,00|0,00
change them to:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere||||||0|1
2|zwol|9,00|9,00|0,00|AAA_01,AAA_03
Do that for all lines in a $LinesToChange
Any help will be much appreciated!
Greetings!
Some strange text file you have there, but anyway, this should do it:
# read in the text file as string array
$txt = Get-Content -Path '<PathToTheTextFile>'
$csv = Import-Csv -Path '<PathToTheCSVFile>' -Delimiter ';'
# loop through the items (rows) in the CSV and find matching lines in the text array
foreach ($item in $csv) {
$match = $txt | Select-String -Pattern ('|{0}|' -f $item.Number) -SimpleMatch
if ($match) {
# update the matching text line (array indices count from 0, so we do -1)
$txt[$match.LineNumber -1] += ('|{0}|{1}' -f $item.A, $item.B)
# update the line following
$txt[$match.LineNumber] += ('|{0}' -f $item.ValueOfB)
}
}
# show updated text on screen
$txt
# save updated text to file
$txt | Set-Content -Path 'Somehere\newfile.txt'

How can I iterate through a .text file, starting from the end using Windows Powershell?

I want to go through a .txt/.log file line by line (with powershell). I have to start at the last line and make my way up to a specific line.
Can somebody help me with this?
Read the file with Get-Content, reverse the array and iterate trough it:
$content = Get-Content -Path "C:\path\to\my\file.txt"
[array]::Reverse($content)
$content | foreach {
$_ # Do something...
}
There are many ways to achieve this. Here is the basic iteration:
#'
Hello
Cruel
World
'# | Out-File demo-file.txt
$content = Get-Content demo-file.txt
for ($lineNumber = $content.Length-1; $lineNumber -ge 0; $lineNumber--) {
"$content[$lineNumber]"
}

How to make changes to file content and save it to another file using powershell?

I want to do this
read the file
go through each line
if the line matches the pattern, do some changes with that line
save the content to another file
For now I use this script:
$file = [System.IO.File]::ReadLines("C:\path\to\some\file1.txt")
$output = "C:\path\to\some\file2.txt"
ForEach ($line in $file) {
if($line -match 'some_regex_expression') {
$line = $line.replace("some","great")
}
Out-File -append -filepath $output -inputobject $line
}
As you can see, here I write line by line. Is it possible to write the whole file at once ?
Good example is provided here :
(Get-Content c:\temp\test.txt) -replace '\[MYID\]', 'MyValue' | Set-Content c:\temp\test.txt
But my problem is that I have additional IF statement...
So, what could I do to improve my script ?
You could do it like that:
Get-Content -Path "C:\path\to\some\file1.txt" | foreach {
if($_ -match 'some_regex_expression') {
$_.replace("some","great")
}
else {
$_
}
} | Out-File -filepath "C:\path\to\some\file2.txt"
Get-Content reads a file line by line (array of strings) by default so you can just pipe it into a foreach loop, process each line within the loop and pipe the whole output into your file2.txt.
In this case Arrays or Array List(lists are better for large arrays) would be the most elegant solution. Simply add strings in array until ForEach loop ends. After that just flush array to a file.
This is Array List example
$file = [System.IO.File]::ReadLines("C:\path\to\some\file1.txt")
$output = "C:\path\to\some\file2.txt"
$outputData = New-Object System.Collections.ArrayList
ForEach ($line in $file) {
if($line -match 'some_regex_expression') {
$line = $line.replace("some","great")
}
$outputData.Add($line)
}
$outputData |Out-File $output
I think the if statement can be avoided in a lot of cases by using regular expression groups (e.g. (.*) and placeholders (e.g. $1, $2 etc.).
As in your example:
(Get-Content .\File1.txt) -Replace 'some(_regex_expression)', 'great$1' | Set-Content .\File2.txt
And for the good example" where [MYID\] might be somewhere inline:
(Get-Content c:\temp\test.txt) -Replace '^(.*)\[MYID\](.*)$', '$1MyValue$2' | Set-Content c:\temp\test.txt
(see also How to replace first and last part of each line with powershell)

Getting last X characters from each line PowerShell

I'm trying to get the last 8 characters of every line of an array in the pipeline,
I thought this would output the last 8 characters but the output seems to be blank
foreach($line in $Texfile)
{
$line[-8..-1]-join ''
}
There's any number of ways to do this, but I opted to pipe Get-Content to ForEach-Object.
Get-Content -Path <Path\to\file.txt> | ForEach-Object {
$_.Substring($_.Length - 8)
}
In your example, you'd use $Line in place of $_ and not pipe to ForEach-Object, and instead, use the Foreach language construct as you've done.
Foreach ($Line in $TextFile) {
$Line.Substring($Line.Length - 8)
}
Try this:
foreach ($Line in $Texfile) {
$Line.Remove(0, ($Line.Length - 8))
}
That works fine. You misspelled $textfile though, with no "t". Maybe that's why you had no output.
Or (-join on the left side):
'1234567890
1234567890
1234567890' | set-content file
$textfile = cat file
foreach($line in $textfile)
{
-join $line[-8..-1]
}
34567890
34567890
34567890

Copy specific lines from a text file to separate file using powershell

I am trying to get all the lines from an Input file starting with %% and paste it into Output file using powershell.
Used the following code, however I am only getting last line in Output file starting with %% instead of all the lines starting with %%.
I have only started to learn powershell, please help
$Clause = Get-Content "Input File location"
$Outvalue = $Clause | Foreach {
if ($_ -ilike "*%%*")
{
Set-Content "Output file location" $_
}
}
You are looping over the lines in the file, and setting each one as the whole content of the file, overwriting the previous file each time.
You need to either switch to using Add-Content instead of Set-Content, which will append to the file, or change the design to:
Get-Content "input.txt" | Foreach-Object {
if ($_ -like "%%*")
{
$_ # just putting this on its own, sends it on out of the pipeline
}
} | Set-Content Output.txt
Which you would more typically write as:
Get-Content "input.txt" | Where-Object { $_ -like "%%*" } | Set-Content Output.txt
and in the shell, you might write as
gc input.txt |? {$_ -like "%%*"} | sc output.txt
Where the whole file is filtered, and then all the matching lines are sent into Set-Content in one go, not calling Set-Content individually for each line.
NB. PowerShell is case insensitive by default, so -like and -ilike behave the same.
For a small file, Get-Content is nice. But if you start trying to do this on heavier files, Get-Content will eat your memory and leave you hanging.
Keeping it REALLY simple for other Powershell starters out there, you'll be better covered (and with better performance). So, something likes this would do the job:
$inputfile = "C:\Users\JohnnyC\Desktop\inputfile.txt"
$outputfile = "C:\Users\JohnnyC\Desktop\outputfile.txt"
$reader = [io.file]::OpenText($inputfile)
$writer = [io.file]::CreateText($outputfile)
while($reader.EndOfStream -ne $true) {
$line = $reader.Readline()
if ($line -like '%%*') {
$writer.WriteLine($line);
}
}
$writer.Dispose();
$reader.Dispose();