Check first character of each line for a specific value in PowerShell - powershell

I am reading in a text file that contains a specific format of numbers. I want to figure out if the first character of the line is a 6 or a 4 and store the entire line in an array for use later. So if the line starts with a six add the entire line into sixArray and if the line starts with a 4 add the entire line into fourArray.
How can I check the first character and then grab the remaining X characters on that line? Without replacing any of the data?

Something like this would probably work.
$sixArray = #()
$fourArray = #()
$file = Get-Content .\ThisFile.txt
$file | foreach {
if ($_.StartsWith("6"))
{
$sixArray += $_
}
elseif($_.StartsWith("4"))
{
$fourArray += $_
}
}

If you're running V4:
$fourArray,$sixArray =
((get-content $file) -match '^4|6').where({$_.startswith('4')},'Split')

Use:
$Fours = #()
$Sixes = #()
GC $file|%{
Switch($_){
{$_.StartsWith("4")}{$Fours+=$_}
{$_.StartsWith("6")}{$Sixes+=$_}
}
}

If it's me I'd just use a regex.
A pattern like this will catch everything you need.
`'^[4|6](?<Stuff>.*)$'`

Related

Powershell- match split and replace based on index

I have a file
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*LM*KY
EF*12*Code1*TX*1234*RJ
I need to replace the 5th element in the CD segment alone from LM to ET in each of the file in the folder. Element delimiter is * as mentioned in the above sample file content. I am new to PowerShell and tried a code as below but unfortunately it is not giving desired results. Can any of you please provide some help?
foreach($xfile in $inputfolder)
{
If ($_ match "^CD\*")
{
[System.IO.File]::ReadAllText($xfile).replace(($_.split("*")[5],"ET") | Set-Content $xfile
}
[System.IO.File]::WriteAllText($xfile),((Get-Content $xfile -join("~")))
}
here's a slightly different way to get there ... [grin] what it does ...
fakes reading in a test file
when ready to do this for real, remove the entire #region/#endregion block and use Get-Content.
sets the constants
iterates thru the imported text file lines
checks for a line that starts with the target pattern
if found ...
== escapes the old value with [regex]::Escape() to deal with the asterisks
== replaces the escaped old value with the new value
== outputs the new version of that line
if NOT found, outputs the line as-is
stores all the lines into the $OutStuff var
displays that on screen
the code ...
#region >>> fake reading in a plain text file
# in real life, use Get-Content
$InStuff = #'
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*LM*KY
EF*12*Code1*TX*1234*RJ
'# -split [System.Environment]::NewLine
#endregion >>> fake reading in a plain text file
$TargetLineStart = 'CD*'
$OldValue = '*LM*'
$NewValue = '*ET*'
$OutStuff = foreach ($IS_Item in $InStuff)
{
if ($IS_Item.StartsWith($TargetLineStart))
{
$IS_Item -replace [regex]::Escape($OldValue), $NewValue
}
else
{
$IS_Item
}
}
$OutStuff
output ...
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*ET*KY
EF*12*Code1*TX*1234*RJ
i will leave saving that to a new file [or overwriting the old one] to the user. [grin]
You could capture all that comes before the match in group 1, and match LM.
In the replacement use $1ET
^(CD*(?:[^*\r\n]+\*){5})LM\b
Regex demo
If you don't want to match LM literally, you could also match any other char than * or a newline.
^(CD*(?:[^*\r\n]+\*){5})[^*\r\n]+\b
Replace example
$allText = Get-Content -Raw file.txt
$allText -replace '(?m)^(CD*(?:[^*\r\n]+\*){5})LM\b','$1ET'
Output
AB*00*Name1First*Name1Last*test
BC*JCB*P1*Church St*Texas
CD*02*83*XY*Fax*ET*KY
EF*12*Code1*TX*1234*RJ

How can I loop through each record of a text file to replace a string of characters

I have a large .txt file containing records where a date string in each record needs to be incremented by 2 days which will then update the field to the right of it which contains dashes --------- with that date. For example, a record contains the following record data:
1440149049845_20191121000000 11/22/2019 -------- 0.000 0.013
I am replacing the -------- dashes with 11/24/2019 (2 days added to the date 11/22/2019) so that it shows as:
1440149049845_20191121000000 11/22/2019 11/24/2019 0.000 0.013
I have the replace working on a single record but need to loop through the entire .txt file to update all of the records. Here is what I tried:
$inputRecords = get-content '\\10.12.7.13\vipsvr\Rancho\MRDF_Report\_Report.txt'
foreach ($line in $inputRecords)
{
$item -match '\d{2}/\d{2}/\d{4}'
$inputRecords -replace '-{2,}',([datetime]$matches.0).adddays(2).tostring('MM/dd/yyyy') -replace '\b0\.000\b','0.412'
}
I get an PS error stating: "Cannot convert null to type "System.DateTime"
I'm sorry but why are we using RegEx for something this simple?
I can see it if there are differently formatted lines in the file, you'd want to make sure you aren't manipulating unintended lines, but that's not indicated in the question. Even still, it doesn't seem like you need to match anything within the line itself. It seems like it's delimited on spaces which would make a simple split a lot easier.
Example:
$File = "C:\temp\Test.txt"
$Output =
ForEach( $Line in Get-Content $File)
{
$TmpArray = $Line.Split(' ')
$TmpArray[2] = (Get-Date $TmpArray[1]).AddDays(2).ToString('M/dd/yyyy')
$TmpArray -join ' '
}
The 3rd element in the array do the calculation and reassign the value...
Notice there's no use of the += operator which is very slow compared to simply assigning the output to a variable. I wouldn't make a thing out of it but considering we don't know how big the file is... Also the String format given before 'mm/dd/yyyy' will result in 00 for the month like for example '00/22/2019', so I changed that to 'M/dd/yyyy'
You can still add logic to skip unnecessary lines if it's needed...
You can send $Output to a file with something like $Output | Out-File <FilePath>
Or this can be converted to a single pipeline that outputs directly to a file using | ForEach{...} instead of ForEach(.. in ..) If the file is truly huge and holding $Output in memory is an issue this is a good alternative.
Let me know if that helps.
You mostly had the right idea, but here are a few suggested changes, but not exactly in this order:
Use a new file instead of trying to replace the old file.
Iterate a line at a time, replace the ------, write to the new file.
Use '-match' instead of '-replace', because as you will see below that you need to manipulate the capture more than a simple '-replace' allows.
Use [datetime]::parseexact instead of trying to just force cast the captured text.
[string[]]$inputRecords = get-content ".\linesource.txt"
[string]$outputRecords
foreach ($line in $inputRecords) {
[string]$newLine = ""
[regex]$logPattern = "^([\d_]+) ([\d/]+) (-+) (.*)$"
if ($line -match $logPattern) {
$origDate = [datetime]::parseexact($Matches[2], 'mm/dd/yyyy', $null)
$replacementDate = $origDate.adddays(2)
$newLine = $Matches[1]
$newLine += " " + $origDate.toString('mm/dd/yyyy')
$newLine += " " + $replacementDate.toString('mm/dd/yyyy')
$newLine += " " + $Matches[4]
} else {
$newLine = $line
}
$outputRecords += "$newLine`n"
}
$outputRecords.ToString()
Even if you don't use the whole solution, hopefully at least parts of it will be helpful to you.
Using the suggested code from adamt8 and Steven, I added to 2 echo statements to show what gets displayed in the variables $logpattern and $line since it is not recognizing the pattern of characters to be updated. This is what displays from the echo:
Options MatchTimeout RightToLeft
CalNOD01 1440151020208_20191205000000 12/06/2019 12/10/2019
None -00:00:00.0010000 False
CalNOD01 1440151020314_20191205000000 12/06/2019 --------
None -00:00:00.0010000 False
this is the rendered output:
CalNOD01 1440151020208_20191205000000 12/06/2019 12/10/2019
CalNOD01 1440151020314_20191205000000 12/06/2019 --------
This is the code that was used:
enter image description here

How to match 3 lines in txt file and extract string in the middle line

Using Powershell I would like to extract a value from text file that is in between two lines that match a pattern.
I'm trying to match 3 lines, 1st & 3rd will always be the same:
1st: ' 1'
2nd: trying to read... always 2-4 characters
3rd: ' 40'
There are multiple occasions where line 1&3 should match this.
I tried with bellow code.
$aa=Get-Content $filename1 -Raw
$aaa=$aa |Where-Object { ( $_ -match '(.\s1)(?:\r\n|[\r\n])*(?:\r\n|[\r\n])(\s40)') }
$aaa
I get too much output...maybe it's matching just 1st and 3rd line and many lines in between.
You can do it using Regex:
$regex = [regex] '\s+1\r?\n(?<secondline>.*)\r?\n\s+40'
$match = $regex.Match($text)
$result = while ($match.Success) {
$match.Groups['secondline'].Value
$match = $match.NextMatch()
}
$result
Where $text is the file you read with $text = Get-Content 'FILENAME' -Raw like this:
1
trying to read... always 2-4 characters
40
1
another second line
40
1
the line you are interested in
40
The result is
trying to read... always 2-4 characters
another second line
the line you are interested in
Regex is usually a poor substitute for a context-sensitive multi-line parser.
Given the document format, I'd simply write one:
$grabLine = $false
switch -File($filename1){
' 1' {
$grabLine = $true
}
' 40' {
$grabLine = $false
}
default{
if($grabLine){
$_
# break here if you only need one line
}
}
}

How to import first two values for each line in CSV file | PowerShell

I have a CSV file that generates everyday, and generates with data such as:
windows:NT:v:n:n:d:n:n:n:n:m:n:n
I should also mention that that example is one of 3,900+ lines, and not every line of data has the same number of "columns". What I'm trying to do is import just the first two "columns" of data into a variable. For this example, it would be "Windows" and "NT", nothing else.
How would I go about doing this? I've tried using -delimiter ':', and not much luck.
The number of lines shouldn't matter.
My approach from comment (to your previous question) should work,
if there is no header and you only want the first two columns,
just specify Header 1,2
> import-csv .\strange.csv -delim ':' -Header (1..2) |Where 2 -eq 'NT'
1 2
- -
windows NT
Example for building the entire array
$Splitted_List = #()
foreach($Line in Get-Content '.\myfilewithuseragents.txt'){
$Splitted = $Line -split ":"
$Splitted_Object = [PSCustomObject]#{
$part1 = $splitted[0]
$part2 = $Splitted[1]
}
$Splitted_List.Add($Splitted_Object) | Out-Null
}
For every line you'll just read the line and with the string from that line, you're easily able to split it
$useragent = "windows:NT:v:n:n:d:n:n:n:n:m:n:n"
Then the first part will be referenced to as $useragent.Split(":")[0], the second as $useragent.Split(":")[1], etc.
Including the for-loop that would be something like
foreach($useragent in Get-Content '.\myfilewithuseragents.txt') {
$splitted = $useragent.Split(":")
$part1 = $splitted[0]
}

Error with Substring Code - Powershell

I'm getting errors with the code below. I'm trying to take lines in a text file that are over 180 characters and parse them to a new line. I have to take the first 22 characters and put it in from of part two since it contains inital data that's needed on the line:
$data = get-content "C:\TestFile.txt"
$strAcct= #()
$strPart1= #()
$strPart2= #()
$strLength= #()
foreach($line in $data)
{
if ( $line.length -gt 181)
{ $strLength = $line.length
$strAcct += $line.substring(0,22)
$strPart1 += $line.substring(0,180)
$strPart2 += $line.substring(181,$strLength)
Add-Content "C:\TestFile-Output.txt" $strPart1
Add-Content "C:\TestFile-Output.txt" $strAcct $strPart2
}
Else {
Add-Content "C:\TestFile-Output.txt" $line
}
}
Substring takes an index, and the number of characters to take starting from that index. Your third substring bombs, because you are trying to take more characters than there are in the string. Change it to $strLength - 181, or alternatively, you can leave the second parameter out entirely, to just take the rest of the string starting from the index in the first parameter.
Change this:
$line.substring(181, $strLength)
to this:
$line.substring(181)
or even this:
$line.substring(181, $strLength - 181)