Need to search a line from a long config file with powershell - regex

In short,
I need to find a specific line in a long file that starts with "set ip " and continues with some parameter that I need to replace. this line appears multiple times in the file so I need to find it between 2 specific lines.
The longer story:
We will soon get to configure many FortiGate Firewalls for our offices, most of the settings, policies etc. will be the same but the external IP changes, some other addresses change etc.
So I'm trying to make a powershell script that will take an existing config (which might change) and find the specific lines I need and replace them.
I tried with regex but couldn't make it work for me on multiple lines.
basically for a reference I need to find the "set ip " in the following part:
config system interface
edit "wan1"
set vdom "root"
set ip 7.7.7.7 255.255.255.252
set allowaccess ping https ssh
set ident-accept enable
set type physical
set scan-botnet-connections block
set alias "WAN1"
set role wan
set snmp-index 1
next
(IP was changed for security) and so on.
what I've got so far is:
get-content .\Fortigate.conf | select-string -pattern "^#","set uuid " -notmatch
sadly nothing I tried to cut that part of text to search only there worked.
for example with regex I tried:
get-content .\Fortigate.conf | select-string -pattern "^#","set uuid " -notmatch | select-string -Pattern '(?m)edit "wan1".*?end'

For this issue, I would not try to regex multiple line but approach it the PowerShell way "Implement for the Middle of a Pipeline" and remember information as which section your in, e.g.:
Read each specific line (if you writing a cmdlet, use the processmethod section):
get-content .\Fortigate.conf | ForEach {...
Remember the current edit section:
$Wan = ($_ | Select-String 'edit[\s]+"(.*)"').Matches.Groups[1].Value
Catch the specific set within the (edit) section:
$SetIP = ($_ | Select-String 'set[\s]+ip[\s]+(.*)').Matches.Groups[1].Value
Make your decisions based on the values and section, e.g.:
If ($Wan -eq $MyWan) {
if (($SetIP -Split "[\s]+") -Contains $MyIP) {...
Drop your new string entry (intermediately) on the pipeline:
Write-Output " set ip $MyNewIP"
Or keep the original string entry:
Else {Write-Output $_}

Related

Comparing two text files is not working in Powershell

I am trying to compare the contents of two text files and have only the differences be outputted to the console.
The first text file is based on the file names in a folder.
$AsyFolder = Get-ChildItem -Path .\asy-data -Name
I then remove the prefix of the file name that is set up by the user and is the same for every file and is separated from the relevant info with a dash.
$AsyFolder| ForEach-Object{$_.Split("-").Replace("$Prefix", "")} | Where-Object {$_}|Set-Content -Path .\templog.txt
The output looks like $Asyfolder Output
bpm.art
gbr.pdf
asy.pdf
fab.pdf
as1.art
odb.tgz
ccam.cad
read_me_asy.txt
There is another file that is the reference and contains the suffixes of files that should be there.
It looks like this Reference File
tpm.art
bpm.art
gbr.pdf
asy.pdf
fab.pdf
as1.art
as2.art
odb.tgz
xyp.txt
ccam.cad
And its contents are received with $AsyTemplate = Get-Content -Path C:\Users\asy_files.txt
The logic is as follows
$AsyTemplate |
ForEach-Object{
If(Select-String -Path .\templog.txt -Pattern $_ -NotMatch -Quiet){
Write-Host "$($_)"
}
}
I have tried various ways of setting up the templog.txt with -InputObject: using Get-Content, Get-Content -Raw, a variable, writing an array manually. I have also tried removing -NotMatch and using -eq $False for the output of select string.
Everytime though the output is just the contents of asy_files.txt (Reference File). It doesn't seem to care what is in templog.txt ($AsyFolder Output).
I have tried using compare-object/where-object method as well and it just says that both files are completely different.
Thank you #Lee_Dailey for your help in figuring out how to properly ask a question...
It ended up being additional whitespace (3 tabs) after the characters in the reference file asy_files.txt.
It was an artifact from where I copied from, and powershell was seeing "as2.art" and "as2.art " I am not 100% as to why that matters, but I found that sorting for any whitespace with /S that appears after a word character /W and removing it made the comparison logic work. The Compare-Object|Where-Object worked as well after removing the whitespace.

Update a line in the AD info field

I have slight problem.
We have a PowerShell script that sets an expiration date in the 'Notes:' field in the AD.
What i want to do is to be able to remove/update this w/o removing other data in the field.
Example of 'Notes:' field (for ie. user X):
GR1234567890 expires on 20251125
END
If i use following code to try and isolate everything but the line starting with GR in it.
$UserName = Get-ADUser -Filter {SAMAccountName -eq "X"} -Properties Info
$UserName.Info | Select-String -Pattern 'GR[\s\S].+' -NotMatch
I get a "full match" and no output at all.
And if i remove -NotMatch i get a full match and full output of 'Notes:' field.
I've tried the RegEx in some of the RegEx online testers out there and there it works as expected. It is like there are no LF/CR or some wierd encoding on the output when traversing the pipeline...
I could do a match GR, a date and everything in between i guess... but id like for knowledge sake want to know if the above thinking is not possible or totally wrong (RegEX is not my strongest suit).
Problem was indeed the code itself as pointed out by Wiktor. Hats of to him.
$UserName.Info -Replace '^GR.+'
Will remove the line i want removed.

How can I split and select from an array of filenames in Powershell?

I have a script I wrote in my company for clearing Citrix UPM profiles. Not very complicated, but it generates logs for every user it is run on. Along the format of:
UPMreset-e0155555-20150112-0733
UPMreset-n9978524-20150114-1128
UPMreset-jsmith-20150113-0840
etc.
So I want to grab the folder with all the .txt files, select only the username and count to see if one appears more than a certain number of times. To check for problem children. Putting them into an array is easy enough, but when doing a -split I can't seem to find a regex combination to select only the username. I thought I could just do a ('-')[1], but that doesn't appear to work. Do you have any suggestions?
$arrFiles = Get-Childitem "c:\logs"
$arrFiles | %{ $arrfile = $_ -split ('-'); Write-Host $arrfile[0]}
edit: Included test code for posterity sake.
I'd try something like this:
$Path = 'N:\Folder\*.txt';
Get-ChildItem $Path |
ForEach-Object {
Write-Output $_.BaseName.Split('-')[1];
} |
Group-Object |
Where-Object { $_.Count -gt 1 } |
Sort-Object -Property Name |
Select-Object Name, Count;
To answer the question.
$_ is one of the objects returned by Get-ChildItem. Those objects are not strings. They're .Net objects of type System.IO.DirectoryInfo or System.IO.FileInfo. That means if we use $_, we're referencing the whole object. Worse, neither of those objects has a Split() method, so $_.Split('-') would refer to a function that didn't exist.
BaseName is a property of a FileInfo or DirectoryInfo object. That property contains the name of the file without the path or the extension. Critically, this property is also a String, which does have the Split() method. So using this property does two things: It removes the path name and the extension since we don't care about that and we don't want it to potentially break something (e.g., if someone put a dash in the parent folder's name), and it gives us a String object which we can manipulate with String methods and do things like call the Split function.
Try something like this at the command line:
$x = Get-ChildItem 'N:\Folder\UPMreset-e0155555-20150112-0733.txt';
$x | Get-Member;
You'll get a huge list of Methods (functions) that the object can do and Properties (attribute values) of the object. Name, FullName, BaseName, and Extension are all very common properties to use. You should also see NoteProperties and CodeProperties, which are added by the PowerShell provider to make using them easier (they wouldn't be available in a C# program). The definition tells you how to call the method or what the type of the property is and what you can do with it. You can usually Google and find MSDN documentation for how to use them, although it's not always the easiest way to do things.
Compare the above to this:
$x.BaseName | Get-Member;
You can see that it's a String, that there all kinds of methods like Split, Replace, IndexOf, etc.
Another helpful one is:
$x | Select-Object *;
This returns all the Propety, NoteProperty, and CodeProperty values this object has.
This highlights one of the best ways to learn about what you can do with an object. Pipe it to Get-Member, and you learn the type and any methods or properties that you can access. That, combined with piping something to Select-Object *, can tell you a lot about what you're working with.
What problem were you having with .split('-')[1]?
$filenames = #(
'UPMreset-e0155555-20150112-0733',
'UPMreset-n9978524-20150114-1128',
'UPMreset-jsmith-20150113-0840'
)
$filenames |% {$_.split('-')[1]}
e0155555
n9978524
jsmith
It looks like the filenames are always UPMreset-, followed by the username. So use this:
UPMreset-(.+?)-
and the capture group will contain the username. It's using a lazy quantifier to get anything up to the next dash.
You could also do the split in a calculated property with Group-Object:
$FileNames = Get-ChildItem -Path $LogDir -Filter "*.txt" -Name
$FileNames | Group-Object #{Expression={($_ -split "-")[1]}} | Where-Object {$_.Count -gt 1}

PowerShell Select-String regular expression to locate two strings on the same line

How do I use Select-String cmdlet to search a text file for a string which starts with a specific string, then contains random text and has another specific string towards the end of the line? I'm only interested in matches across a single line in the text file, not across the entire file.
For example I am searching to match both 'Set-QADUser' and 'WhatIf' on the same line in the file. And my example file contains the following line:
Set-QADUser -Identity $($c.ObjectGUID) -ObjectAttributes #{extensionattribute7=$ekdvalue} -WhatIf | Out-Null
How do I use Select-String along with a Regular Expression to locate the pattern in question? I tried using the following and it does work but it also matches other instances of either 'Set-QADUser' or 'WhatIf' found elsewhere in the text file and I only want to match instances when both search strings are found on the same line.
Select-String -path "test.ps1" -Pattern "Set-QADUser.*WhatIf" | Select Matches,LineNumber
To make this more complicated I actually want to perform this search from within the script file that is being searched. Effectively this is used to warn the user that the script being run is currently set to 'WhatIf' mode for testing. But of course the regEx matches the text from the actual Select-String cmd within the script when it's run - so it finds multiple matches and I can't figure out a very good way to overcome that issue. So far this is what I've got:
#Warn user about 'WhatIf' if detected
$line=Select-String -path $myinvocation.mycommand.name -Pattern "Set-QADUser.*WhatIf" | Select Matches,LineNumber
If ($line.Count -gt 1)
{
Write-Host "******* Warning ******"
Write-Host "Script is currently in 'WhatIf' mode; to make changes please remove '-WhatIf' parameter at line no. $($line[1].LineNumber)"
}
I'm sure there must be a better way to do this. Hope somebody can help.
Thanks
If you use the -Quiet switch on Select-String it will just return a boolean True/False, depending on whether it found a match or not.
-Quiet <SwitchParameter>
Returns a Boolean value (true or false), instead of a MatchInfo object. The value is "true" if the pattern is found; otherwise, the value is "false".
Required? false
Position? named
Default value Returns matches
Accept pipeline input? false
Accept wildcard characters? false

Powershell using a regex to create a new hash table object

I'm trying to parse a file that contains blocks of text in this format(there can be 1-n of these):
-----------------------------------------------------------
KB Article Number(s): 2028769, 2072493, 2120979, 2143880, 2163958, 2163980, 980653, 980883, 981155, 981867, 982321, 982850
Language: All (Global)
Platform: x64
Location: (http://foo.bar.com)
Password: foo
-----------------------------------------------------------
The text is coming from an MS Hotfix request email if anyone is interested.
I have the following powershell one liner:
$file | select-string "(?<Name>\w+):(?<Value>.*)" -allmatches | SELECT #{N="Name";E={$_.Matches[0].Groups[1].Value}}, #{N="Value";E={$_.Matches[0].Groups[2].Value}}
This give me a flat set of name value pairs, I would like it to return an array of hashtables with each of the 5 Keys populated.
I'm not sure if the problem is my regex or how I'm accessing the matches (There must be a less verbose way of picking these out).
It's not pretty but I think it will work for you:
$ht = #{}
$file | %{if ($_ -match '(?<Name>[^:]+):(?<Value>.*)') {
$ht.($matches.Name) = $matches.Value.Trim()} `
elseif ($ht.Count) {$ht;$ht.Clear()}}
Using the -match operator is a tad easier because you can use the $matches variable directly without having to go through Select-String's MatchInfo.Matches property. BTW I'm assuming here $file was populated by a call to Get-Content i.e. each line is a string in a string array.