I need to edit txt file using PowerShell. The problem is that I need to apply changes for the string only if the remaining part of the string matches some pattern. For example, I need to change 'specific_text' to 'other_text' only if the line ends with 'pattern':
'specific_text and pattern' -> changes to 'other_text and pattern'
But if the line doesn't end with pattern, I don't need to change it:
'specific_text and something else' -> no changes
I know about Replace function in PowerShell, but as far as I know it makes simple change for all matches of the regex. There is also Select-String function, but I couldn't combine them properly. My idea was to make it this way:
((get-content myfile.txt | select-string -pattern "pattern") -Replace "specific_text", "other_text") | Out-File myfile.txt
But this call rewrites the whole file and leaves only changed lines.
You may use
(get-content myfile.txt) -replace 'specific_text(?=.*pattern$)', "other_text" | Out-File myfile.txt
The specific_text(?=.*pattern$) pattern matches
specific_text - some specific_text...
(?=.*pattern$) - not immediately followed with any 0 or more chars other than a newline as many as possible and then pattern at the end of the string ($).
I'm attempting to match events where the only way to tell when an event starts and ends is with the header or first value in the multi line event (e.g. START--). Basically, using the header as an ending anchor to get the whole event. Also, the last event will end at the end of the file, so there's no anchor for that one. I'm not quite sure how to make this work.
Event Example (There's no spaces between the lines)
START--random stuff here
more random stuff on this new line
more stuff and things
START--some random things
additional random things
blah blah
START--data data more data
START--things
blah data
$FileContent | select-string '^START--(.*?)^START--' -AllMatches | Foreach {$_.Matches} | Foreach {$_.Value}
You may read in the file into a single variable (it can be done by passing -Raw option to Get-Content, for example) and split it at the start of lines starting with START-- but the first line:
$contents = Get-Content 'your_file_path' -Raw
$contents -split '(?m)^(?!\A)(?=START--)'
It will yield
Regex details
(?m) - the multiline option is ON
^ - now, it matches start of lines due to (?m)
(?!\A) - not the start of the whole string/text
(?=START--) - the location that is immediately followed with START-- substring.
I have been trying to extract certain equal to 40 values get the sixth last word from multiple lines inside a .txt file with PowerShell.
I have code so far :
$file = Get-Content 'c:\temp\file.txt'
$Array = #()
foreach ($line in $file)
{
$Array += $line.split(",")[6]
}
$Array
$Array | sc "c:\temp\export2.txt"
Txt file : (may be duplicate lines such as hostname01)
4626898,0,3,0,POL,INCR,hostname01,xx,1549429809,0000000507,1549430316,xxx,0,40,1,xxxx,51870834,5040,100
4626898,0,3,0,POL,INCR,hostname02,xx,1549429809,0000000507,1549430316,xxx,0,15,1,xxxx,51870834,5040,100
4626898,0,3,0,POL,INCR,hostname03 developer host,xx,1549429809,0000000507,1549430316,xxx,0,40,1,xxxx,51870834,5040,100
4626898,0,3,0,POL,INCR,hostname01,xx,1549429809,0000000507,1549430316,xxx,0,40,1,xxxx,51870834,5040,100
This is what I want :
hostname01
hostname02
hostname03 developer host
This is not a fast solution, but a convenient and flexible one:
Since your text file is effectively a CSV file, you can use Import-Csv.
Since your data is missing is a header row (column names), which we can supply to Import-Csv via its -Header parameter.
Since you're interested in columns number 7 (hostnames) and 14 (the number whose value should be 40), we need to supply column names (of our choice) for columns 1 through 14.
Import-Csv conveniently converts the CSV rows to (custom) objects, whose properties you can query with Where-Object and selectively extract with Select-Object; adding -Unique suppresses duplicate values.
To put it all together:
Import-Csv c:\temp\file.txt -Header (1..14) |
Where-Object 14 -eq 40 |
Select-Object -ExpandProperty 7 -Unique
For convenience we've named the columns 1, 2, ... using a range expression (1..14), but you're free to use descriptive names.
Assuming that c:\temp\file.txt contains your sample data, the above yields:
hostname01
hostname03 developer host
To output to a file, pipe the above to Set-Content, as in your question:
... | Set-Content c:\temp\export2.txt
If the desired field is always the 6th in the line it is easier to split each line and fetch the 6th member:
Get-Content 'c:\temp\file.txt' | Foreach-Object {($_ -split ',')[6]} | Select-Object -Unique
You could use a non-capturing group to look through the string for the correct format and reference the name of your 6 element with the 1st capture group $1:
(?:\d+,\d,\d,\d,[A-Z]+,[A-Z]+,)([a-zA-Z 0-9]+)
Demo here
(?: ) - Specifies a non-capture group (meaning it's not referenced via $1, or $2 like you normally would with a capture group
\d+, (I won't repeat all of these, but) looking for a one or more digits followed by a literal ,.
[A-Z]+, - Finds an all capital letter string, followed by a literal , (this occurs twice).
([a-zA-Z 0-9]+) - The capture group you're looking for, $1, that will capture all characters a-z, A-Z, spaces, and digits up until a character not in this set (in this case, a comma). Giving you the text you're looking for.
Below should work with what you are trying to do
Get-Content 'c:\temp\file.txt' | %{
$_.Split(',')[6]
}| select -Unique
I am trying to parse some large log files to detect occurrences of a coding bug. Identifying the defect is finding a sequence of strings on different lines with a date in between. I am terrible at describing things so posting an example:
<Result xmlns="">
<Failure exceptionClass="processing" exceptionDetail="State_Open::Buffer Failed - none">
<SystemID>ffds[sid=EPS_FFDS, 50] Version:01.00.00</SystemID>
<Description>Lo
ck Server failed </Description>
</Failure>
</Result>
</BufferReply>
7/22/2017 8:41:15 AM | SomeServer | Information | ResponseProcessing.TreatEPSResponse() is going to process a response or event. Response.ServiceID [Server_06] Response.Response [com.schema.fcc.ffds.BufferReply]
I will be searching for multiple instances of this sequence through multiple logs: Buffer Failed on followed by Server_#.
The Server_# can be any 2-digit number and will never be on the same line.
Buffer failed will never repeat prior to Server_# being found.
The date and time that is in between but guessing that if this is possible it would be captured also.
Ideally, I would pipe something like this to another file
Buffer Failed - none" 7/22/2017 8:41:15 AM [Server_06]
I have attempted a few things like
Select-String 'Failed - none(.*?)Response.Response' -AllMatches
but it doesn't seem to work across lines.
Select-String can only match text spanning multiple lines if it receives the input as a single string. Plus, . normally matches any character except line feeds (\n). If you want it to match line feeds as well you must prefix your regular expression with the modifier (?s). Otherwise you need an expression that does include line feeds, e.g. [\s\S] or (.|\n).
It might also be advisable to anchor the match at expressionDetail rather than the actual detail, because that makes the match more flexible.
Something like this should give you the result you're looking for:
$re = '(?s)exceptionDetail="(.*?)".*?(\d+/\d+/\d+ \d+:\d+:\d+ [AP]M).*?\[(.*?)\] Response\.Response'
... | Out-String |
Select-String -Pattern $re -AllMatches |
Select -Expand Matches |
ForEach-Object { '{0} {1} [{2}]' -f $_.Groups[1..3] }
The expression uses non-greedy matches and 3 capturing groups for extracting exception detail, timestamp and servername.
I am looking to extract the name value from the output returned by the line...
Gwmi win32_groupuser –computer $env:computername | ? {$_.groupcomponent –like '*"Administrators"'} | Select -Property PartComponent
I would think that I would use a regular expression to trim but I am unfamiliar with their operation and (as of yet) have been unable to find instructions sufficient to complete this.
For reference, the output is something like...
\\My_Machine\root\cimv2:Win32_UserAccount.Domain="My_Machine",Name="Administrator"
And I would like to extract 'Administrator' from that output.
Right now I'm trying...
$Report = Gwmi win32_groupuser –computer $env:computername | ? {$_.groupcomponent –like '*"Administrators"'} | Select -Property PartComponent
$Report | ForEach-Object {$_.PartComponent -match '(?<=Name=")[^"]+[^"]'
[PSCustomObject]#{Resultant_String=$Matches.Values}}
and I'm getting...
Resultant_String
{Administrator}
True
{admin}
True
{GroupName}
True
{UserName}
True
{CiscoHistRprtUsr}
Try something like this, (?<=Name=")[^"]+[^"]. What this is doing is a positive look behind for Name=" and then any char (except ") in repetition and then any char that isn't ". This is a little more robust, since this will still work if the Name= is not the last element in your string. If Name= is say the first value returned, it should still only capture what is in the quotes directly following it, and not everything else until the last ".
Using your test example, I did some testing here.
If Name is always at the end of the string, you can simply use
([^"]*)"$
Explanation here : http://regex101.com/r/yV3uD6
Two solutions:
(?<=Name=")[^"]*?(?=")
Note that the the ? after the * is important here. It makes the quantifier non-greedy (it's just a fancy way to say that it will capture as few characters as possible and never go beyond the closing ").
(?:Name=")([^"]*?)(?:")
Since the first and the last groups are non-capturing, you just have to retrieve the value of the second group (which is actually the first and only one in terms of capture) with something like \1 or $1.
Ultimately I ended up with this.
$FileServer = "My_File_Server"
$LocalHostName = $env:computername
$OutPutPath = "\\$FileServer\system_information$\"
$GetAdmins = Gwmi win32_groupuser –computer $LocalHostName | ? {$_.groupcomponent –like '*"Administrators"'} | Select -Property PartComponent
ForEach-Object{$GetAdmins | % {if ($_ -match 'Name="(.+)"') {[PSCustomObject]#{Name=$Matches[1]}}}} | Export-Csv -Path "$OutPutPath\$LocalHostName\$ReportName.csv" -NoTypeInformation}
It produces a table with a name header that has the username of all the local admins extracted from the returned string.