Get Previous Line while reading Log File in Powershell - powershell

I have a log file which I am reading and getting what I need. But I have an additional trouble.
I want to get the line which is before the line which has keyword "Error"
I tried to use
Get-Content -Path $File -Tail 2 | Select-String -Pattern $Patterns -Context 1,0 -SimpleMatch
This is also giving me an output which I need. But after doing that I want to extract data from the two lines. Can I use a foreach loop on the output?
$Patterns = #('execution ended')
$File= "C:\GProcess\log.txt"
#-Context 1,0
Get-Content -Path $File -Tail 2 | Select-String -Pattern $Patterns -Context 1,0 -SimpleMatch
Current Output is
Process failed as there was an error
> Process execution ended
Maybe I just want Process failed as there was an error but search for keyword "execution ended"
I hope I have explained my query properly.

You can get just the line(s) of the context directly.
$match = Get-Content -Path $File | Select-String -Pattern $Patterns -Context 1,0 -SimpleMatch
$match.Context.PreContext

The below code will give you the line number of your string search, rather than using -tail.
If you have that then you can code the extra bit you need to grab the line above or below quite easily.
(Get-Content -Path 'C:\GProcess\log.txt' | Select-String -Pattern 'error').LineNumber

Related

How can I use ForEach-Object output for Select-String directly?

I have several single line xml files out of which I want to extract the contents of a particular tag <LIFNR>. I managed doing that using the following two lines of power shell:
get-content P:\Webservice\21*_*CR_d*.xml | foreach-object { $_ -replace '>', ">`r`n" } > xmls_newline.txt
get-content .\xmls_newline.txt | Select-String -Pattern '.*<\/LIFNR>'
However if I want to skip the step of creating an intermediary text file I cannot achieve the same result.
$xml = get-content P:\Webservice\21*_*CR_d*.xml | foreach-object { $_ -replace '>', ">`r`n" }
$xml | Select-String -Pattern '.*<\/LIFNR>'
or
get-content P:\Webservice\21*_*CR_d*.xml | foreach-object { $_ -replace '>', ">`r`n" } | Select-String -Pattern '.*<\/LIFNR>'
each just print the result of the string splitting in the foreach-object statement, the Select-String command is ignored.
Can somebody explain to me what is different about the latter two attempts compared to the working solution?
Get-Content outputs each line as a separate string. To emulate the same behavior, split the string after > instead of appending a linebreak:
Get-Content P:\Webservice\21*_*CR_d*.xml |ForEach-Object { $_ -split '(?<=>)' } | Select-String -Pattern '.*<\/LIFNR>'
The construct (?<=...) is a lookbehind assertion - this way, PowerShell will split on a zero-length string immediately after >

Powershell search directory for code files with text matching input a txt file

Data mapping project, in house system to new vendor system. First step is find all the occurrences of current database field names (or column names to be precise) in the C# .cs source files. Trying to use Powershell. Have recently created PS searches with Get-ChildItem and Select-String that work well but the search string array was small and easily hard coded inline. But the application being ported has a couple hundred column names and significant amounts of code. So armed with a text file of all the column names Pipleline would seem like a god tool to create a the basic cross ref for further analysis. However, I was not able to get the Pipeline to work with an external variable anyplace other than first step. Trying using -PipelineVariable, $_. and global variable. Did not find anything specific after lots of searching. P.S. This is my first question to StackoOverflow, be kind please.
Here is what I hoped would work but do dice so far.
$inputFile = "C:\DataColumnsNames.txt"
$outputFile = "C:\DataColumnsUsages.txt"
$arr = [string[]](Get-Content $inputfile)
foreach ($s in $arr) {
Get-ChildItem -Path "C:ProjectFolder\*" -Filter *.cs -Recurse -ErrorAction SilentlyContinue -Force |
Select-String $s | Select-Object Path, LineNumber, line | Export-csv $outputfile
}
Did find that this will print the list one time but not twice. In fact it seems using the variable in this way results in processing simply skipping any further pipeline steps.
foreach ($s in $arr) {Write-Host $s | Write $s}
If it isn't possible to do this in Powershell easily my fallback is to do with C# although would much rather get the level up with PowerShell if anyone can point me to the correct understanding of how to do things in the Pipepline, or alternatively construct an equivalent function. Seems like such a natural fit for Powershell.
Thanks.
You're calling Export-csv $outputfile in a loop, which rewrites the whole file in every iteration, so that only the last iteration's output will end up in the file.
While you could use -Append to iteratively append to the output file, it is worth aking a step back: Select-String can accept an array of patterns, causing a line that matches any of them to be considered a match.
Therefore, your code can be simplified as follows:
$inputFile = 'C:\DataColumnsNames.txt'
$outputFile = 'C:\DataColumnsUsages.txt'
Get-ChildItem C:\ProjectFolder -Filter *.cs -Recurse -Force -ea SilentlyContinue |
Select-String -Pattern (Get-Content $inputFile) |
Select-Object Path, LineNumber, line |
Export-csv $outputfile
-Pattern (Get-Content $inputFile) passes the lines of input file $inputFile as an array of patterns to match.
By default, these lines are interpreted as regexes (regular expressions); to ensure that they're treated as literals, add -SimpleMatch to the Select-String call.
This answer to a follow-up question shows how to include the specific pattern among the multiple ones passed to -Pattern that matched on each line in the output.
I think you want to append each occurrence to the csv file. And you need to get the content of the file. Try this:
$inputFile = "C:\DataColumnsNames.txt"
$outputFile = "C:\DataColumnsUsages.txt"
$arr [string[]](Get-Content $inputfile)
foreach ($s in $arr) {
Get-ChildItem -Path "C:ProjectFolder\*" -Filter *.cs -Recurse -ErrorAction SilentlyContinue -Force | Foreach {
Get-Content "$_.Fullname" | Select-String $s | Select-Object Path, LineNumber, line | Export-csv -Append -Path "$outputfile"
}
}
-Append was not introduced before powershell v3.0 (Windows 8) then try this:
$inputFile = "C:\DataColumnsNames.txt"
$outputFile = "C:\DataColumnsUsages.txt"
$arr [string[]](Get-Content $inputfile)
foreach ($s in $arr) {
Get-ChildItem -Path "C:ProjectFolder\*" -Filter *.cs -Recurse -ErrorAction SilentlyContinue -Force | Foreach {
Get-Content "$_.Fullname" | Select-String $s | Select-Object Path, LineNumber, line | ConvertTo-CSV -NoTypeInformation | Select-Object -Skip 1 | Out-File -Append -Path "$outputfile"
}
}

To check the file contents line by line

I need to check the contents line by line by selecting the string of the line.
I have declared each line matching string to a variable.Is it possible to use switch cases for looking into the variables
I have used if condition to check the each line with the help of variables .
I wanted to know whether we could use switch cases?
Consider file.txt having following contents :
$q = Get-Content -Path .\file.txt |Select-String 'Hello' -SimpleMatch
$w = Get-Content -Path .\file.txt |Select-String 'new' -SimpleMatch
$e = Get-Content -Path .\file.txt |Select-String 'World' -SimpleMatch
$r = Get-Content -Path .\file.txt |Select-String 'Hi' -SimpleMatch
$t = Get-Content -Path .\file.txt |Select-String 'greet' -SimpleMatch
So can we check the variables using the switch options. Is it possible to output the variables which are not present. How can we achieve the same if the file contents are large?
Can we use switch options and output the desired result?
If you just want to know which of the 5 words are not in any of the lines in the file, you can do this
$Pattern = 'Hello|new|World|Hi|greet'
$Test = (Get-Content -Path .\file.txt | Select-String -Pattern $Pattern -AllMatches).foreach{$_.matches.Value}
$($pattern -split '\|').where{$Test -notcontains $_}
But is that your goal?

Scan txt file for multiple strings and save the following lines

I have a problem that I am trying to solve, however, due to my non existing PowerShell knowledge it is proving to be harder than I hoped. So any help would be appreciated.
The problem can be simplified as:
Find a string in a txtfile
Extract the information on the row after that string
Store the information in a handle
Find a second string in the txtfile and repeat the procedure
Store both strings in a new file or delete everything else in the txt file.
I am then trying to do this for approx 20k files. I would love to have the information under their keyword and comma delimited so that I can import them in other systems.
My files look somewhat like the following
random words
that are unimportant
Keyword
FirstlineofNumbersthatIwanttoExtract
random words again that are unimportant
Secondkeyword
SecondLineOfNumbersThatIWantToExtract
end of the file
All files are however not similar in terms of the row that the lines I want to extract are on. I would the output to be something like
Keyword, SecondKeyword
FirstLineOfNumbersThatIWantToExtract, SecondLineOfNumbersThatIWantToExtract
And done. I got this far
[System.IO.DirectoryInfo]$folder = 'C:\users\xx\Desktop\mappcent3'
foreach ($file in ($folder.EnumerateFiles())) {
if ($file.Extension -eq '.txt') {
$content = Get-Content $file
$FirstRegex = 'KeyWordOne
(.+)$'
$First_output = "\1"
$test = Select-String -Path $file.FullName -Pattern $FirstRegex
}
}
This would do something similar to what you are asking. This requires PowerShell 3.0+
$path = 'C:\users\xx\Desktop\mappcent3'
$firstKeyword = "Keyword"
$secondKeyword = "Secondkeyword"
$resultsPath = "C:\Temp\results.csv"
Get-ChildItem $path -Filter "*.txt" | ForEach-Object{
# Read the file in
$fileContents = Get-Content $_.FullName
# Find the first keyword data
$firstKeywordData = ($fileContents | Select-String -Pattern $firstKeyword -Context 0,1 -SimpleMatch).Context.PostContext[0]
# Find the second keyword data
$secondKeywordData = ($fileContents | Select-String -Pattern $secondKeyword -Context 0,1 -SimpleMatch).Context.PostContext[0]
# Create a new object with details gathered.
[pscustomobject][ordered]#{
File = $_.FullName
FirstKeywordData = $firstKeywordData
SecondKeywordData = $secondKeywordData
}
} | Export-CSV $resultsPath -NoTypeInformation
Select-String is what does most of the magic here. We take advantage of -Context which consumes lines before and after the match. We want the one following so that is why we use 0,1. Wrap that up in a custom object and then we can export it to a CSV file.
Keyword Overlap
Beware that your keywords can overlap and create odd results in your output files. In your sample Keyword matches multiple lines so the result set would reflect that.
If you did just want to write back to the original file you could easily do that as well
"$firstKeywordData,$secondKeywordData" | Set-Content $_.FullName
Or something similar.
The Select-String cmdlet has a -Context parameter that makes it easy to extract lines before or after the line on which there's a match.
You can use Export-Csv to export to the format you require (although with 20K files you may want to write directly to the output files)
foreach($file in Get-ChildItem C:\users\xx\Desktop\mappcent3 |Where {-not $_.PsIsContainer})
{
$FirstKeyword = 'FirstKeyword'
$FirstLine = Select-String -Path $file.FullName -Pattern $FirstKeyword -Context 0,1 |Select -Expand Context -First 1 |Select -Expand PostContext
$SecondKeyword = 'SecondKeyword'
$SecondLine = Select-String -Path $file.FullName -Pattern $SecondKeyword -Context 0,1 |Select -Expand Context -First 1 |Select -Expand PostContext
New-Object psobject -Property #{$FirstKeyword=$FirstLine;$SecondKeyword=$SecondLine} |Export-Csv (Join-Path $file.DirectoryName ($file.BaseName + '_keywords.txt'))
}

Powershell: Querying multiple strings and outputting to both user and file

I've been given the duty of validating installation of an update across many hosts. This validation is performed by querying for a string of an error code that signifies success. I would like this output to both appear in the shell and also be written to a file.
$computerList = #($userInput)
foreach ($_ in $computerList){
get-content -tail 20 ("filepath") `
| where {$_| select-string "All steps complete!"} `
| where {$_| select-string "Output Error = 0 "} `
| out-file C:\users\me\Desktop\validation_log.txt -append
}
I based the multiple string "grep"-ing off of an online article,
However, this doesn't write the desired strings to the out-file path, nor does it display in console.
Can anyone please explain the best method for querying multiple strings and then outputting those to a file?
Your example is more complex than necessary.
You could just chain the Select-String. And Tee-Object is the way to go if you want to output something to both file and down the pipeline:
PS C:\temp> Get-Content -LiteralPath ".\input.txt"
All steps complete!
All steps complete! Output Error = 0
asdf
PS C:\temp> Get-Content -LiteralPath ".\input.txt" | Select-String -Pattern "All steps" | Select-String -Pattern "Output Error" | ForEach-Object {$_.ToString()} | Tee-Object -FilePath ".\output.txt" -Append
All steps complete! Output Error = 0
PS C:\temp> Get-Content -LiteralPath ".\output.txt"
All steps complete! Output Error = 0
The above behaves like an logical "and" for each pattern. If you want to "or" the patterns you could use the fact that the pattern is a regular expression:
PS C:\temp> Get-Content -LiteralPath ".\input.txt" | Select-String -Pattern "All steps|Output Error" | ForEach-Object {$_.ToString()} | Tee-Object -FilePath ".\output.txt" -Append
All steps complete!
All steps complete! Output Error = 0
Also notice that Select-String outputs Microsoft.PowerShell.Commands.MatchInfo objects and not strings. You will probably get unwanted newlines in your output if you pipe these directly to Tee-Object. I therefore convert these to strings in the Foreach-Object