Joining value from Context parameter to Line property from Select-String output - powershell

I'm trying to compile log errors from multiple files in a single directory. The error messages are included over the span of two lines. I would like to concatenate both lines into a single line/object and then export all errors into a a neat csv.
I'm attempting to accomplish this with the Select-String utility, and the -Context parameter. Prior to piping the results through the Select-Object utility, everything's Kosher. However, Once I pipe the results through Select-Object or Export-CSV, the -Context line is lost.
$trigger = 'ERROR'
$folderPath = 'C:\Users\test\Desktop\testpath'
$logFiles = gci -Path $folderPath -Filter *.txt -File
$logFiles | Select-String -Pattern $trigger -CaseSensitive -SimpleMatch -Context 0,1 | Select-Object LineNumber, Line, Filename |
Export-Csv -Path .\$(Get-Date -Format yyyymmddhhmmss).csv -Encoding UTF8 -NoTypeInformation
Omitting the Select-Object and Export-Csv Cmdlets yields the desired, raw, results with the friendly right angle bracket '>' (ASCII 62). The raw results can even be exported via the Out-File Cmdlet, no problem.
However, what I would like to do, is combine the Pattern line with the Context line, creating a single object, which would eventually be output as a csv for further analysis.
I would like apologize if this question seems trivial. I've scoured resources trying to figure this out and unfortunately haven't been able to. Thanks in advance!

Pipe select-string through fl * to see what the properties are.
$a = ls log | select-string error -context 0,1
$a | fl *
IgnoreCase : True
LineNumber : 2
Line : error
Filename : log
Path : /Users/js/log
Pattern : error
Context : Microsoft.PowerShell.Commands.MatchInfoContext
Matches : {0}
$a.context
PreContext PostContext DisplayPreContext DisplayPostContext
---------- ----------- ----------------- ------------------
{} {after } {} {after }
This worked for me:
ls log | select-string error -context 0,1 | select linenumber, line,
#{n='PostContext'; e={$_.context.postcontext}}, filename
LineNumber Line PostContext Filename
---------- ---- ----------- --------
2 error after log

Related

Replace method is not working on my string variables

I wrote a script to extract the URL and Revision Number from svn info command of a svn repository and save the result in a .txt file.
The $revision and $url are both strings, so the replace method should work on them but it doesn't. Is there possibly something wrong in my code causing this?
$TheFilePath = "C:\Users\MyPC\REPOSITORY\NewProject\OUTPUT.txt"
echo "#- Automatic Package Update `n----------"| Out-File -FilePath $TheFilePath
$url = svn info C:\Users\MyPC\REPOSITORY\NewProject\trunk | Select-String -Pattern 'URL' -CaseSensitive -SimpleMatch | select-object -First 1
$url | Add-Content -path $TheFilePath
$revision = svn info C:\Users\MyPC\REPOSITORY\NewProject\trunk | Select-String -Pattern 'Revision' -CaseSensitive -SimpleMatch | $revision.Replace('Revision','srcrev')
$revision | Add-Content -path $TheFilePath
here is the output of svn info (Irrelevant outputs have been omitted) :
Path: .
Working Copy Root Path: C:\Users\MyPC\REPOSITORY\NewProject\trunk
URL: https://svn.mycompany.de/svn/NewProject/trunk
Relative URL: ^/trunk
Repository Root: https://svn.mycompany.de/svn/NewProject
Revision: 5884
And here is what I get inside the .txt file , running the code :
#- Automatic Package Update
----------
URL: https://svn.mycompany.de/svn/NewProject/trunk
Revision: 5884
----------
Looking at the example output of svn info here and the example you just provided, you should be able to get the info you need easier with ConvertFrom-StringData then with Select-String.
In PowerShell < 7.x you can use ConvertFrom-StringData on the output of svn info after changing the colon (:) delimiter into an equal sign (=) to get a Hashtable with all properties and values.
Then, using calculated properties you can extract the items you're interested in and save as CSV file for instance like this:
$svnInfo = svn info 'C:\Users\MyPC\REPOSITORY\NewProject\trunk'
$result = $svnInfo -replace '(?<!:.*):', '=' | ConvertFrom-StringData |
Select-Object #{Name = 'URL'; Expression = {$_['URL']}},
#{Name = 'srcrev'; Expression = {$_['Revision']}}
# output on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path 'C:\Users\MyPC\REPOSITORIES\NewProject\OUTPUT.csv' -NoTypeInformation
Regex details on the -replace to replace only the first occurrence of the colon:
(?<! Assert that it is impossible to match the regex below with the match ending at this position (negative lookbehind)
: Match the character “:” literally
. Match any single character
* Between zero and unlimited times, as many times as possible, giving back as needed (greedy)
)
: Match the character “:” literally
If you're using PowerShell 7 or higher, tghings get easier because then you have an extra -Delimiter parameter:
$svnInfo = svn info 'C:\Users\MyPC\REPOSITORY\NewProject\trunk'
$result = $svnInfo -replace '(?<!:.*):', '=' | ConvertFrom-StringData -Delimiter ':' |
Select-Object #{Name = 'URL'; Expression = {$_['URL']}},
#{Name = 'srcrev'; Expression = {$_['Revision']}}
# output on screen
$result | Format-Table -AutoSize
# output to CSV file
$result | Export-Csv -Path 'C:\Users\MyPC\REPOSITORIES\NewProject\OUTPUT.csv' -NoTypeInformation
$revision doesn't exist until after the svn command is done.
Use the ForEach-Object cmdlet and refer to the current match as $_ to modify the output object inline - the matched line in the output from Select-String is stored in a property called Line:
$revision = svn info C:\Users\MyPC\REPOSITORY\NewProject\trunk |Select-String -Pattern 'Revision' -CaseSensitive -SimpleMatch |ForEach-Object { $_.Line.Replace('Revision', 'srcrev') }

How do I edit a #{ that powershell automatically puts registry keys ins?

so I have a powershell command that grabs a registry key. But when using select-pattern, it always sticks the whole key within a line, and I've been googling everywhere but no where is telling me how to only grab certain language from the line, and not return the full like. Like below:
Line : #{t0_recursive=\\ad\; t7_recursive=\\ad\chrolit; t5_recursive=\\ad\arolit; t3_recursive=k:\;
t9_recursive=\\ad\fwrolit; t10_recursive=\\ad\larolit; t15_recursive=\\ad\slrolit;
t6_recursive=\\ad\brolit; t11_recursive=\\ad\mirolit; t14_recursive=\\ad\sfrolit;
t12_recursive=\\ad\nyrolit; t2_recursive=j:\; t16_recursive=\\ad\enfcases; t1_recursive=f:\;
t8_recursive=\\ad\drolit; t4_recursive=m:\; t13_recursive=\\ad\plrolit; t17_recursive=C:\ENFProcessing;
t129=c:\enfprocessing; t99=\\ad\slrolit; t9=\\ad\; t84=\\ad\sfrolit; t69=\\ad\plrolit; t54=\\ad\nyrolit;
t39=\\ad\mirolit; t279_recursive=C:\Users; t264=\\ad\fwrolit; t249=\\ad\drolit; t24=\\ad\larolit;
t234=\\ad\chrolit; t219=\\ad\brolit; t204=\\ad\arolit; t159_recursive=\\AD.SEC.GOV\Projects;
t144_recursive=\\AD.SEC.GOV\users; t114=\\ad\enfcases;
PSPath=Microsoft.PowerShell.Core\Registry::HKLM\SOFTWARE\Policies\Adobe\Adobe
Acrobat\DC\FeatureLockDown\cTrustedFolders\cAlwaysTrustedForJavaScript;
PSParentPath=Microsoft.PowerShell.Core\Registry::HKLM\SOFTWARE\Policies\Adobe\Adobe
Acrobat\DC\FeatureLockDown\cTrustedFolders; PSChildName=cAlwaysTrustedForJavaScript;
PSProvider=Microsoft.PowerShell.Core\Registry}
I get this output, but can't just return PSPath and the C:\Users* from the line, it always returns the full line.
Why is this?
$value = Get-ChildItem -Path 'Registry::HKEY_CURRENT_USER\SOFTWARE\Adobe\Adobe Acrobat\DC\TrustManger\cTrustedFolder' 2>NULL | findstr : | measure-object -line | select-object -expandproperty lines ; if ( $value -lt 1 ) { echo 'PASSED, HKEY_CURRENT_USER\SOFTWARE\Adobe\Adobe Acrobat\DC\TrustManger\cTrustedFolder is NULL' } else { $value2 = Get-ChildItem -Path 'Registry::HKEY_CURRENT_USER\SOFTWARE\Adobe\Adobe Acrobat\DC\TrustManger\cTrustedFolder' -Recurse | ForEach-Object {Get-ItemProperty -Path $_.PSPath -Name t*} |Select-String -Pattern 'c:\\users*' | select -ExpandProperty Line| findstr /i /V /c:Desktop, /c:downloads|format-list LineNumber,Line
I tried googling how to edit lines in powershell, to no avail.
Take out findstr. It turns objects to strings.
The get-childitem output is a bit confusing for the registry. The formatting file $PSHOME\Registry.format.ps1xml runs get-itemproperty. Use get-itemproperty to get registry values.
I have a replacement for get-itemproperty that can be used for recursive searches here: Use PowerShell to search for string in registry keys and values
For example:
get-childitem -recurse hklm:\software\adobe | get-itemproperty2 |
? name -like distiller*
Path Name Value Type
---- ---- ----- ----
HKLM:\software\adobe\Acrobat Distiller\10.0\Uninstall Distiller 1 DWord

create file index manually using powershell, tab delimited

Sorry in advance for the probably trivial question, I'm a powershell noob, please bear with me and give me advice on how to get better.
I want to achieve a file index index.txt that contains the list of all files in current dir and subdirs in this format:
./dir1/file1.txt 07.05.2020 16:16 1959281
where
dirs listed are relative (i.e. this will be run remotely and to save space, the relative path is good enough)
the delimiter is a tab \t
the date format is day.month.fullyear hours:minutes:seconds, last written (this is the case for me, but I'm guessing this would be different on system setting and should be enforced)
(the last number is the size in bytes)
I almost get there using this command in powershell (maybe that's useful to someone else as well):
get-childitem . -recurse | select fullname,LastWriteTime,Length | Out-File index.txt
with this result
FullName LastWriteTime Length
-------- ------------- ------
C:\Users\user1\Downloads\test\asdf.txt 07.05.2020 16:19:29 1490
C:\Users\user1\Downloads\test\dirtree.txt 07.05.2020 16:08:44 0
C:\Users\user1\Downloads\test\index.txt 07.05.2020 16:29:01 0
C:\Users\user1\Downloads\test\test.txt 07.05.2020 16:01:23 814
C:\Users\user1\Downloads\test\text2.txt 07.05.2020 15:55:45 1346
So the questions that remain are: How to...
get rid of the headers?
enforce this date format?
tab delimit everything?
get control of what newline character is used (\n or \r or both)?
Another approach could be this:
$StartDirectory = Get-Location
Get-ChildItem -Path $StartDirectory -recurse |
Select-Object -Property #{Name='RelPath';Expression={$_.FullName.toString() -replace [REGEX]::Escape($StartDirectory.ToString()),'.'}},
#{Name='LastWriteTime';Expression={$_.LastWriteTime.toString('dd.MM.yyyy HH:mm:ss')}},
Length |
Export-Csv -Path Result.csv -NoTypeInformation -Delimiter "`t"
I recommend to use proper CSV files if you have structured data like this. The resulting CSV file will be saved in the current working directory.
If the path you are running this from is NOT the current scrip path, do:
$path = 'D:\Downloads' # 'X:\SomeFolder\SomeWhere'
Set-Location $path
first.
Next, this ought to do it:
Get-ChildItem . -Recurse -File | ForEach-Object {
"{0}`t{1:dd.MM.yyyy HH:mm}`t{2}" -f ($_ | Resolve-Path -Relative), $_.LastWriteTime, $_.Length
} | Out-File 'index.txt'
On Windows the newline will be \r\n (CRLF)
If you want control over that, this should do:
$newline = "`n" # for example
# capture the lines as string array in variable $lines
$lines = Get-ChildItem . -Recurse -File | ForEach-Object {
"{0}`t{1:dd.MM.yyyy HH:mm}`t{2}" -f ($_ | Resolve-Path -Relative), $_.LastWriteTime, $_.Length
}
# join the array with the chosen newline and save to file
$lines -join $newline | Out-File 'index.txt' -NoNewline
Because your requirement is to NOT have column headers in the output file, I'm using Out-File here instead of Export-Csv

Inserting Space/Separator after Select-String -Context

I have a script that reads series of log files located in different places and looks for an error code with Select-String. After the error code I print out the next four lines to a file with "-Context". That file's content gets dumped into an email and sent off.
$logsToCheck = "F:\Log1.log",
"F:\log2.log",
"F:\log3.log"
$logsToCheck | % {Select-String -Path $_ -Pattern "SQLCODE:2627" -Context
0, 4} | Out-File $dupChkFile
$emailbody = Get-Content $dupChkFile | ConvertTo-Html
The actual output of the string is poorly formatted and runs together. Is there a way to add blank lines or spaces after the last line when using Select-String -Context?
Originally I was piping the $emailbody to a Out-String but changed it to HTML to try to clear up formatting.
try reading out the match and context separately.
Select-String -Path $_ "SQLCODE:2627" -Context 0,2 | %{
$_.Line
$_.Context.PostContext
"-----Separator-----"
}
the default output of Select-String with Context is human-readable modified, this will return everything as an array of unmodified strings, so you can be sure there will always be newlines, and it will behave better with other cmdlt's including Out-String or loops.
I would suggest using concatenation:
% { "$(Select-String -Path $_ -Pattern 'SQLCODE:2627' -Context 0, 4)`r`n" } |
Out-File $dupChkFile -Append

Powershell Select-String parse email from .rtf to .csv

Parse .rtf file, output email addresses in .csv file?
I have an .rtf file containing a bunch of email addresses, I need this parsed so that I can compare a .csv file to active users in Active Directory.
Basically I want what is to the left of "#my.domain.com"
$finds = Select-String -Path "path\to\my.rtf" -Pattern "#my.domain.com" | ForEach-Object {$_.Matches}
$finds | Select-Object -First 1 | ft *
This of course gives me one result so that I don't have alot of output.
I only manage to get matches or the complete line.
I've tried adding something along the line of
$finds = Select-String -Path "path\to\my.rtf" -Pattern "\w.#my.domain.com"
This gives me the very two last letters in the addresses.
If I keep adding dots to the "wildcard"
-Pattern "\w.....#my.domain.com"
I also get a ton of numbers/characters (.rtf formatting) for addresses that contains fewer characters.
How do I do this?
EDIT: I will update the question as soon as I've found a solution. As of now I'm trying with regular expressions.
Example:
-Pattern "\w*?#my.domain.com"
$mPattern = "[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+(\.[a-zA-Z]{2,4})"
$lines = get-content "path\to\your.rtf"
foreach($line in $lines){
([regex]::MAtch($rtfInput, $mpattern, "IgnoreCase ")).value }
This code worked for me. My inital code but with a new search pattern.
$finds = Select-String -Path "path\to\my.rtf" -Pattern "[a-zA-Z0-9._%+-]+#[a-zA-Z0-9.-]+(\.[a-zA-Z]{2,4})" | ForEach-Object {$_.Matches}
$finds | Select-Object -First 10 | ft *
Thanks!