I have a text file in which it has lots of headings and a few sentences below it.
I have wanted to search for the heading and if the heading is available I want to copy sentences below the heading till next heading.
Is it possible in PowerShell please help me I tried
$linenumber= Get-Content "C:\Users\KSYEDSU\Documents\temp\4491309.txt" | select-string $search
Select-String $string $dataRead -Context 1, $linenumber| % { $_.Context.PostContext } | out-file "C:\Users\KSYEDSU\Documents\temp\Results.txt"
But it is throwing an error telling it is expecting interger
$linenumber= Get-Content "C:\Users\KSYEDSU\Documents\temp\4491309.txt" | select-string $search
Select-String $string $dataRead -Context 1, $linenumber| % { $_.Context.PostContext } | out-file "C:\Users\KSYEDSU\Documents\temp\Results.txt"
ex:
Heading A
1234
34545
13213
Heading B
So I will search for Heading A and if it is available then start copying from 1234... till 13213.
Select-String will find the string inside your text but does not return the position as int. You could loop trough your file and look manueally for the heading and collect the data between.
#Get file content as string array
[System.String[]]$FileContent = Get-Content -Path 'C:\Users\KSYEDSU\Documents\temp\4491309.txt'
#For each line in the file
for ($i = 0; $i -lt $FileContent.Count; $i ++)
{
#If the line equals your start header
if ($FileContent[$i] -eq 'Heading A')
{
$i ++ #Get the next line
#Return line until end header appears
while ($FileContent[$i] -ne 'Heading B')
{
$FileContent[$i] #Return line
$i ++ #Get next line
}
}
}
You could use regex to do this, so you don't have to loop through all lines in the text file:
$headingStart = 'Heading A'
$headingEnd = 'Heading B'
# get the file content in one string, including all newline characters
$content = Get-Content "C:\Users\KSYEDSU\Documents\temp\4491309.txt" -Raw
# since the real headings may contain characters that have special meaning in regex, we escape them
$regex = '(?s)^{0}\s+(.*)\s{1}' -f [regex]::Escape($headingStart), [regex]::Escape($headingEnd)
if ($content -match $regex) {
$matches[1] | Out-File -FilePath "C:\Users\KSYEDSU\Documents\temp\Results.txt"
}
else {
Write-Host "No text found between $headingStart and $headingEnd"
}
Using your example, the resulting file will contain:
1234
34545
13213
You can use a switch statement combined with the -Regex and -File flags.
$insideHeaders = $false
. {switch -Regex -File "C:\Users\KSYEDSU\Documents\temp\4491309.txt" {
'Heading A' { $insideHeaders = $true }
'Heading B' { return }
default {
if ($insideHeaders) { $_ }
}
}} | Out-File "C:\Users\KSYEDSU\Documents\temp\Results.txt"
Explanation:
Each value between single quotes is a regex string. You will have to backslash (\) escape any special regex characters, which can be done automatically using [regex]::Escape(string).
When the bottom header (Heading B in this case) is reached, the return statement will exit the switch statement.
All lines not matching one of the headers will trigger the default condition. The default condition will only output a line if the first header has been found.
Related
I have multiple text documents, each with multiple lines of information, and I'm trying to replace a single line of text within each document with text of my choosing. The single line of text that is to be replaced does not have a consistent length or set of characters across the multiple documents. Also, the placement of this line of text is not always located at the same place within the document. The only consistent factor here is the string directly above the line of text to be replaced is the same string across all documents - "Courier". I'm trying to use the word "Courier" as my reference point with which I'd replace the subsequent line of text with something of my choosing.
Any help would be greatly appreciated! Thanks in advance!
Below I have included the script that I've created so far; however, I am reaching the limits of my capability to complete this. Currently, the script executes successfully without errors, but the line I'm trying to replace does not get replaced - Instead, the text I'm looking to input as the replacement is entered below "Courier" and the text I don't need (that I'd like to be replaced) is moved down the document, now located directly under the new text I've entered in my script. Here's an example of what I get when I run my script in its current state:
Courier
Entry location 153
Sidewalk0156378
In this case, "Sidewalk0156378" is the old text that used to be directly under "Courier" before the script was ran, and it needs to be replaced. "Entry location 153" is the new text that should be taking the place of "Sidewalk0156378".
$path = "C:\temp"
if(!(Test-Path $path)){
New-Item -ItemType Directory -Force -Path $path
}
$currentCourier = "C:\Temp\currentCourier.txt"
$editCourier = "C:\Temp\editCourier.txt"
$newCourier = "C:\Temp\newCourier.txt"
Get-Content $currentCourier | ForEach-Object {
$_
if ($_ -match 'Courier') {
"Entry location"
}
} | Set-Content $editCourier
$oldEntry = Get-Content $editCourier
$rem = #()
#("Courier") | ForEach-Object {
$rem += $oldEntry[(($oldEntry | Select-String -Pattern "$_").LineNumber)..(($oldEntry | Select-String -Pattern "$_").LineNumber+1)]
}
Compare-Object $oldEntry $rem | Select-Object -ExpandProperty InputObject | Set-Content $newEntry
The following uses a switch to process the file line by line in combination with the -Wildcard parameter to match any line having the key word.
& {
$skipNext = $false
switch -Wildcard -File $currentCourier {
# if line contains Courier
'*Courier*' {
# output this line
$_
# set this variable to skip next line
$skipNext = $true
# output new value that replaces next line
'new value here'
# skip next conditions
continue
}
# if the bool was set in previous iteration
{ $skipNext } {
# set it to false again
$skipNext = $false
# and go next
continue
}
# else, output line as is
Default { $_ }
}
} | Set-Content $newCourier
Using the logic above with a hardcoded example:
$content = '
line 1
line 2
line 3 has Courier
line 4 should be replaced
line 5
' -split '\r?\n'
$skipNext = $false
switch -Wildcard ($content) {
'*Courier*' {
$_
$skipNext = $true
'Entry location 153'
continue
}
{ $skipNext } {
$skipNext = $false
continue
}
Default { $_ }
}
Output to the console would become:
line 1
line 2
line 3 has Courier
Entry location 153
line 5
Hello my input file will be like below,my requiremnet is to add 4 line if the macthing pattern folled by the next pattern is unmacthed along with count number.
i will check look for the socket and if matches will incrremnt the line count to +1 toi get the next line and look for the word "address",if the address is not present i need to insert a set of line "communication.manageraddress_9,communication.manageraddress_10,communication.manageraddress_11" netx to the line.
communication.manageraddress_7=xxx.com
communication.managerid_7=xxx
communication.managerport_7=xxx
communication.socket_7=xx
communication.manageraddress_8=xxx.com
communication.managerid_8=xxx
communication.managerport_8=xxx
communication.socket_8=plain
Added by Manager
communication.managerhealthmon_4=true
communication.protocolrev_4=3
communication.managerhealthmon_1=true
communication.protocolrev_1=2
output will be like this
communication.manageraddress_7=xxx.com
communication.managerid_7=xxx
communication.managerport_7=xxx
communication.socket_7=xx
communication.manageraddress_8=xxx.com
communication.managerid_8=xxx
communication.managerport_8=xxx
communication.socket_8=plain
communication.manageraddress_9=xxx.com
communication.managerid_9=xxx
communication.managerport_9=xxx
communication.socket_9=plain
communication.manageraddress_10=xxx.com
communication.managerid_10=xxx
communication.managerport_1o=xxx
communication.socket_1o=plain
Added by Manager
communication.managerhealthmon_4=true
communication.protocolrev_4=3
communication.managerhealthmon_1=true
communication.protocolrev_1=2
this my script and i am struck with insert into text file along with increment number,can some one help in power shell.
$files = $File = 'C:\Users\rseerala\Desktop\ARUN\in.txt'
#$NewContent = Get-Content -Path $File
foreach($file in $files){
$content = Get-Content $file
for($i = 0; $i -lt $content.Count; $i++){
$line = $content[$i]
if ($line.Contains("socket"))
{
$line = $content[$i+2]
if ($line.Contains("address"))
{
Write-Host "This line starts with 6"
}}}}
Ok, so if I understand correctly, this is what you want:
#read the file as a single multiline string
$txt = Get-Content -Path 'C:\Users\rseerala\Desktop\ARUN\in.txt' -Raw
# if it contains the magic word '.socket_' followed by a number
if ($txt -match '\.socket_\d+') {
# first split off the 'Added by Manager' stuff
$content, $managerAdded = ($txt -split 'Added by Manager').Trim()
# split the content part into separate blocks of 4 lines
$blocks = $content -split '(\r?\n){2}' | Where-Object { $_ -match '\S' }
# get the index value from the last block
$index = [int]([regex] '(?i)\.socket_(\d+)').Match($blocks[-1]).Groups[1].Value
# now repeat the blocks you already have and output copies with incremented indices
$newBlocks = ($blocks | ForEach-Object {
$_ -replace '_\d+=', ('_{0}=' -f ++$index)
}) -join "`r`n`r`n"
# finally, combine the content part with the new blocks
# and the 'Added by Manager' lines with double newlines
$result = $content, $newBlocks, 'Added by Manager', $managerAdded -join "`r`n`r`n"
# output on screen
$result
# write to a new file
$result | Set-Content -Path 'C:\Users\rseerala\Desktop\ARUN\out.txt'
}
else {
Write-Warning "The file does not contain the word '.socket_' followed by a number.."
}
Output:
communication.manageraddress_7=xxx.com
communication.managerid_7=xxx
communication.managerport_7=xxx
communication.socket_7=xx
communication.manageraddress_8=xxx.com
communication.managerid_8=xxx
communication.managerport_8=xxx
communication.socket_8=plain
communication.manageraddress_9=xxx.com
communication.managerid_9=xxx
communication.managerport_9=xxx
communication.socket_9=plain
communication.manageraddress_10=xxx.com
communication.managerid_10=xxx
communication.managerport_10=xxx
communication.socket_10=plain
Added by Manager
communication.managerhealthmon_4=true
communication.protocolrev_4=3
communication.managerhealthmon_1=true
communication.protocolrev_1=2
$data = Select-String -Path $selectedDirectory\$sqlFile -Pattern "GRANT" -Context 5,5
I want to use PowerShell to read .SQL files and we want to make sure that a user isn't using GRANT or DROP or DELETE without a human reviewing the file to see if it's okay.
My 1 line only is looking at GRANT but I don't think it's working.
If the keywords are in the file, I want to display a portion of the text on the screen +/- 5 lines of where the offending text was found.
Is there a way to change the color of the text for the specific line that has the offending search criteria (all other lines will be shown as default)
If you want colors displayed to the console, you will need to utilize Write-Host.
$data = Select-String -Path $selectedDirectory\$sqlFile -Pattern "GRANT|DROP|DELETE" -Context 5,5
$data | Foreach-Object {
$_.Context.Precontext
Write-Host $_.Line -ForeGroundColor Cyan
$_.Context.Postcontext
}
I'll give it a shot.
This function takes a file, searches for those keywords, and then prints +/- 5 lines. It's easy enough that I'm sure you know how it works and how to modify it. You can find the reference for the matchinfo class (returned by Select-String( here.
Function Get-SQLForbiddenWords ($sqlDataFile) {
$data = Select-String -Path $sqlDataFile -Pattern "GRANT|DROP|DELETE"
Foreach ( $line in $data) {
$lineNumberS = $line.LineNumber - 5
$lineNumberE = $line.LineNumber + 5
echo ('Bad Command Detected: {0}' -f $line.line)
(Get-Content $sqlDataFile)[$lineNumberS..$lineNumberE]
echo "`n"
}
}
It was pretty fun. Output:
Bad Command Detected: DROP
this is the sixth line
GRANT
this is the seventh line
this is the eighth line
DROP
this is the ninth line
this is the tenth linet
this is the eleventh line
this is the twelfbthfbth line
For starters, "GRANT" should be in quotes to denote a string.
If you notice, $line = Select-String -Pattern "Grant" will return an object.
If you look at the properties of the object using Get-Member, one of them is LineNumber
If you have read the contents of your file using $data = Get-Content File.sql or any something similar, you will have your data as an array object. Now you can now use this line number to extract the +/- 5 lines as you wish like $data[50..60]. This will show output lines from 50th to 60th line. You can easily replace the 50 and 60 with your variables.
Another way is to use the oss function (=Out-String -Stream).
Select-String "\b(GRANT|DROP|DELETE)\b" .\test.txt -Context 5 | oss | foreach { Write-Host $_ -ForegroundColor ("White","Cyan")[$_[0] -eq '>'] }
The following may make it a bit more readable.
Select-String "\b(GRANT|DROP|DELETE)\b" .\test.txt -Context 5 | Out-String -Stream | foreach {
if(-not $_) { return }
$fileName,$lineNumber,$line = $_.Split(":", 3)
$color = if($_.StartsWith(">")) { "Cyan" } else { "White" }
Write-Host $fileName $lineNumber.PadLeft(3, "0") $line -ForegroundColor $color -Separator " "
}
I came upon the problem where we need to compare contents two files a.txt and b.txt line by line and output the result if any difference found along with content and line number.
We should not use Compare-Object in this scenario. Do we have any alternative?
I tried using for loops but unable to get desired result
For ex : a.txt:
Hello = "Required"
World = 5678
Environment = "new"
Available = 9080.90
b.txt"
Hello = "Required"
World = 5678.908
Environment = "old"
Available = 6780.90
I need to get the output as:
Line number 2:World is not matching
Line number 3:Environment is not matching
Line number 4:Available is not matching
I tried with the following code snippet but was unsuccessful
$file1 = Get-Content "C:\Users\Desktop\a.txt"
$file2 = Get-Content "C:\Users\Desktop\b.txt"
$result = "C:\Users\Desktop\result.txt"
$file1 | foreach {
$match = $file2 -match $_
if ( $match ){
$match | Out-File -Force $result -Append
}
}
As you seem to have an adverse reaction to Compare-Object, lets try this extremely janky set-up. As you have little to no requirements listed, this will give you the bare minimum to meet your conditions of 'any difference found'.
Copy and paste more If statements should you have more lines.
$a = get-content C:\a.txt
$b = get-content C:\b.txt
If($a[0] -ne $b[0]) {
"Line number 1:Hello is not matching" | Out-Host
}
If($a[1] -ne $b[1]) {
"Line number 2:World is not matching" | Out-Host
}
If($a[2] -ne $b[2]) {
"Line number 3:Environment is not matching" | Out-Host
}
If($a[3] -ne $b[3]) {
"Line number 4:Available is not matching" | Out-Host
}
Get-Content returns the file content as an array of strings with a zero based index.
The array variable has an automatic property .Count/.Length
you can use to iterate the arrays with a simple counting for.
You need to split the line at the = to separate name and content.
Use -f format operator to output the results.
## Q:\Test\2019\05\21\SO_56231110.ps1
$Desktop = [environment]::GetFolderPath('Desktop')
$File1 = Get-Content (Join-Path $Desktop "a.txt")
$File2 = Get-Content (Join-Path $Desktop "b.txt")
for ($i=0;$i -lt $File.Count;$i++){
if($File1[$i] -ne $File2[$i]){
"Line number {0}:{1} is not matching" -f ($i+1),($File1[$i] -split ' = ')[0]
}
}
Sample output:
Line number 2:World is not matching
Line number 3:Environment is not matching
Line number 4:Available is not matching
I have following Input lines in my notepad file.
example 1 :
//UNION TEXT=firststring,FRIEND='ABC,Secondstring,ABAER'
example 2 :
//UNION TEXT=firststring,
// FRIEND='ABC,SecondString,ABAER'
Basically, one line can span over two or three lines. If last character is , then it is treated as continuation character.
In example 1 - Text is in one line.
In example 2 - same Text is in two lines.
In example 1, I can probably write below code. However, I do not know how to do this if 'Input text' spans over two or three lines based on continuation character ,
$result = Get-Content $file.fullName | ? { ($_ -match firststring) -and ($_ -match 'secondstring')}
I think I need a way so that I can search text in multipl lines with '-and' condition. something like that...
Thanks!
You could read the entire content of the file, join the continued lines, and then split the text line-wise:
$text = [System.IO.File]::ReadAllText("C:\path\to\your.txt")
$text -replace ",`r`n", "," -split "`r`n" | ...
# get the full content as one String
$content = Get-Content -Path $file.fullName -Raw
# join continued lines, split content and filter
$content -replace '(?<=,)\s*' -split '\r\n' -match 'firststring.+secondstring'
If file is large and you want to avoid loading entire file into memory you might want to use good old .NET ReadLine:
$reader = [System.IO.File]::OpenText("test.txt")
try {
$sb = New-Object -TypeName "System.Text.StringBuilder";
for(;;) {
$line = $reader.ReadLine()
if ($line -eq $null) { break }
if ($line.EndsWith(','))
{
[void]$sb.Append($line)
}
else
{
[void]$sb.Append($line)
# You have full line at this point.
# Call string match or whatever you find appropriate.
$fullLine = $sb.ToString()
Write-Host $fullLine
[void]$sb.Clear()
}
}
}
finally {
$reader.Close()
}
If file is not large (let's say < 1G) Ansgar Wiechers answer should do the trick.