Powershell replace function - replacing lines with numbers - powershell

Basicly I am trying to find a way to put a variable for replacing a line in powershell.
The current script:
$switches = get-outlookinbox | where subject -eq "Hello"
$e = $switches.body
$e = $e.replace("Hello:","")
$e = $e.replace(" Number","")
$e = $e.replace(":1","")
$e = $e.replace(":2","")
$e = $e.replace(":3","")
$e = $e.replace(":4","")
$e = $e.replace(":99","")
You can see what I am going for here... But I don't want 99 lines of replace code, any thoughts on this?
Also, the numbers MUST have : infront of it, otherwise the replace will corrupt the file since it contains only IP's and ports, it's the ports I want to remove from the output.

You can use a simple foreach loop and iterate from 99 to 1:
foreach ($n in 99..1)
{
$e = $e.Replace(":$n", " ")
}
Or, if you prefer one line:
foreach ($n in 99..1) { $e = $e.Replace(":$n", " ") }
Demo:
PS > $mystr = "a:1:2:3:4:5:6:7:8:9:10:11:12:13:14:15:16:17:18:19:20b"
PS > foreach ($n in 20..1) { $mystr = $mystr.replace(":$n", "") }
PS > $mystr
ab
PS >

Regular expressions : regex101.com
$e = $e -replace ':\d+',""
No loops necessary

Might as well get 'em all while you're at it:
$e -replace 'Hello:| Number|:\d{1,2}'

Related

Powershell - output a foreach loop to one line

I am trying to take the output of my foreach loop and apply the array to a string that reads on one line. Here is my code so far:
$upper = 65..90
$lower = 97..122
foreach ($i in $upper)
{
[char]$i
}
foreach ($i in $lower)
{
[char]$i
}
I'm guessing I need to convert the output of the scriptblock to a variable and use the -join option, but everywhere I look I'm struggling to find how to structure that. Any guidance would be appreciated.
For this particular case, ForEach(type convertToType) is very useful, here is a cool way to get your lower and upper case dictionary string:
$lowerDict = [string]::new(([int][char]'a'..[int][char]'z').ForEach([char]))
$upperDict = $lowerDict.ToUpper()
If you have access to PowerShell Core, it can be reduced to:
$lowerDict = [string]::new('a'..'z')
$upperDict = $lowerDict.ToUpper()
As for what you are struggling on, how to do it with what you currently have (a foreach loop). You can capture all the output from the loop first:
$upper = foreach ($i in 65..90) { [char]$i }
Now, $upper is an array of chars, then to convert it to string, you can either use -join (guessed right) or [string]::new(...) as I did on my previous example:
$upperDict = -join $upper
# OR
$upperDict = [string]::new($upper)

Powershell - F5 iRules -- Extracting iRules

I received a config file of a F5 loadbalancer and was asked to parse it with PowerShell so that it creates a .txt file for every iRule it finds. I'm very new to parsing and I can't seem to wrap my head around it.
I managed to extract the name of every rule and create a separate .txt file, but I am unable to wring the content of the rule to it. Since not all rules are identical, I can't seem to use Regex.
Extract from config file:
ltm rule /Common/irule_name1 {
SOME CONTENT
}
ltm rule /Common/irule_name2 {
SOME OTHER CONTENT
}
What I have for now
$infile = "F5\config_F5"
$ruleslist = Get-Content $infile
foreach($cursor in $ruleslist)
{
if($cursor -like "*ltm rule /*") #new object started
{
#reset all variables to be sure
$content=""
#get rulenames
$rulenameString = $cursor.SubString(17)
$rulename = $rulenameString.Substring(0, $rulenameString.Length -2)
$outfile = $rulename + ".irule"
Write-Host $outfile
Write-Host "END Rule"
#$content | Out-File -FilePath "F5/irules/" + $outfile
}
}
How can I make my powershell script read out what's between the brackets of each rule? (In this case "SOME CONTENT" & "SOME OTHER CONTENT")
Generally parsing involves converting a specific input ("string") into an "object" which PowerShell can understand (such as HTML, JSON, XML, etc.) and traverse by "dotting" through each object.
If you are unable to convert it into any known formats (I am unfamiliar with F5 config files...), and need to only find out the content between braces, you can use the below code.
Please note, this code should only be used if you are unable to find any other alternative, because this should only work when the source file used is code-correct which might not give you the expected output otherwise.
# You can Get-Content FileName as well.
$string = #'
ltm rule /Common/irule_name1 {
SOME CONTENT
}
ltm rule /Common/irule_name2 {
SOME OTHER CONTENT
}
'#
function fcn-get-content {
Param (
[ Parameter( Mandatory = $true ) ]
$START,
[ Parameter( Mandatory = $true ) ]
$END,
[ Parameter( Mandatory = $true ) ]
$STRING
)
$found_content = $string[ ( $START + 1 ) .. ( $END - 1 ) ]
$complete_content = $found_content -join ""
return $complete_content
}
for( $i = 0; $i -lt $string.Length; $i++ ) {
# Find opening brace
if( $string[ $i ] -eq '{' ) {
$start = $i
}
# Find ending brace
elseif( $string[ $i ] -eq '}' ) {
$end = $i
fcn-get-content -START $start -END $end -STRING $string
}
}
For getting everything encompassed within braces (even nested braces):
$string | Select-String '[^{\}]+(?=})' -AllMatches | % { $_.Matches } | % { $_.Value }
To parse data with flexible structure, one can use a state machine. That is, read data line by line and save the state in which you are. Is it a start of a rule? Actual rule? End of rule? By knowing the current state, one can perform actions to the data. Like so,
# Sample data
$data = #()
$data += "ltm rule /Common/irule_name1 {"
$data += "SOME CONTENT"
$data += "}"
$data += "ltm rule /Common/irule_withLongName2 {"
$data += "SOME OTHER CONTENT"
$data += "SOME OTHER CONTENT2"
$data += "}"
$data += ""
$data += "ltm rule /Common/irule_name3 {"
$data += "SOME DIFFERENT CONTENT"
$data += "{"
$data += "WELL,"
$data += "THIS ESCALATED QUICKLY"
$data += "}"
$data += "}"
# enum is used for state tracking
enum rulestate {
start
stop
content
}
# hashtable for results
$ht = #{}
# counter for nested rules
$nestedItems = 0
# Loop through data
foreach($l in $data){
# skip empty lines
if([string]::isNullOrEmpty($l)){ continue }
# Pick the right state and keep count of nested constructs
if($l -match "^ltm rule (/.+)\{") {
# Start new rule
$state = [rulestate]::start
} else {
# Process rule contents
if($l -match "^\s*\{") {
# nested construct found
$state = [rulestate]::content
++$nestedItems
} elseif ($l -match "^\s*\}") {
# closing bracket. Is it
# a) closing nested
if($nestedItems -gt 0) {
$state = [rulestate]::content
--$nestedItems
} else {
# b) closing rule
$state = [rulestate]::stop
}
} else {
# ordinary rule data
$state = [rulestate]::content
}
}
# Handle rule contents based on state
switch($state){
start {
$currentRule = $matches[1].trim()
$ruledata = #()
break
}
content {
$ruledata += $l
break
}
stop {
$ht.add($currentRule, $ruledata)
break
}
default { write-host "oops! $state" }
}
write-host "$state => $l"
}
$ht
Output rules
SOME CONTENT
SOME OTHER CONTENT
SOME OTHER CONTENT2
SOME DIFFERENT CONTENT
{
WELL,
THIS ESCALATED QUICKLY
}

Powershell split sections by regex

In order to create a script which allows me to perform a firewall migration, i have the need to understand how to split an output into sections with Powershell.
The firewall (which is a Sonicwall, if it helps) produces an output, which is delimited by sections. For example:
--System Information--
[Data]
--Network Interfaces--
[Data]
--User Objects Table--
[Data]
...
You can see that the output is delimited by these sections, for which i have produced a regex:
$regex1='^--(\w*|\w+ \w+|\w+ \w+ \w+|\w+ \w+ \w+ \w+)--$'
I don't understand however, how can i produce an output which helps me put a specific section title above, and the data directly below. I don't want all of them, just specific outputs from specific sections.
Any help would be much appreciated,
Thanks a lot in advance
A complex multi-line regex might be a bit to much in your case. A very simple approach would be to go through the content line by line:
$content = #"
--System Information--
[Data1]
--Network Interfaces--
[Data2]
[Data3]
--User Objects Table--
[Data4]
"# -split [System.Environment]::NewLine
$dataDict = #{}
foreach ($line in $content)
{
# Each section opens a new entry in the $dataDict hash table.
# Anything else that follows, gets added to this entry.
if($line -match '^--(.+)--$')
{
$section = $Matches[1]
$dataDict[$section] = #()
}
else
{
$dataDict[$section] += $line
}
}
# You can now narrow down the resulting object to the properties,
# that you are interested in.
[pscustomobject]$dataDict |
Select-Object 'System Information', 'Network Interfaces' |
Format-List
I would prefer an approach with a data table:
$configFile = 'C:\sonciwall\sonicwall.txt'
$dt = New-Object system.Data.DataTable
[void]$dt.Columns.Add('Section',[string]::empty.GetType() )
[void]$dt.Columns.Add('Data',[string]::empty.GetType() )
foreach( $line in Get-Content $configFile ) {
$line = $line.Trim()
if( !$line ) {
Continue # skip empty lines
}
elseif( $line -like '--*' ) {
$section = $line
Continue
}
else {
$data = $line
}
$newRow = $dt.NewRow()
$newRow.Section = $section
$newRow.Data = $data
[void]$dt.Rows.Add( $newRow )
}
# Get specific information from a specific section using sql syntax:
$dt.Select("Section = '--System Information--' AND Data = 'foo'")
# Update specific information in all secions:
$rows = $dt.Select("Data = 'foo'")
foreach( $row in $rows ) {
$row.Data = 'foo bar'
[void]$dt.AcceptChanges()
}

How do I do a conditional replace in PowerShell?

Powershell Newbie looking for some help.
I've got a powershell script which changes a priority value inside a text file to have a value of 50:
if ($line.contains("Priority") )
{
$LastComma = $line.LastIndexOf(",") +1
$N = $line.Substring(0,$LastComma)
$N = $N + "50"
$lines[$counter] = $N
}
This works fine and does exactly what I want it to, but I now need to modify it so it changes the priority value to 45 if there is also the following line present:
Provider = XYZ
If this Provider value is not XYZ then all Priority values are set to 50 as before. Anyone have any advice on how can I achieve this?
Thanks
This example illustrates using Powershell with several concepts:
Matching each line using regular expressions
Replacing each line using regular expressions
Using a switch statement
Here's a test file:
Set-Content test.txt "Provider=ABC,Priority=3
Provider=DEF,Priority=4
Not a provider
Provider=XYZ,Priority=5"
You can transform the test file line by line using the
switch statement (thanks #TheIncorrigible1)
switch -Regex -File test.txt
{
'Provider=XYZ.+Priority='
{
$_ -replace "Priority=\d+","Priority=45"
continue
}
'Priority='
{
$_ -replace "Priority=\d+","Priority=50"
continue
}
default
{
$_
continue
}
}
Please make the effort to provide a working example next time.
Without any more information than what was provided, here is a draft of a solution:
foreach ($line in $lines) {
$value = "XYZ"
$check = $false
if ($line.contains("Provider = $value")) {
$check = $true
}
if ($line.contains("Priority")) {
if ($check) {
$priority = 45
} else {
$priority = 50
}
$LastComma = $line.LastIndexOf(",") +1
$N = $line.Substring(0,$LastComma)
$N = $N + "$priority"
$lines[$counter] = $N
}
}
Remark: This assume that the Provider tag will be found prior to the Priority tags.

Is there a better way to convert all control characters to entities in PowerShell 5?

Context: Azure, Windows Server 2012, PowerShell 5
I've got the following code to convert all control characters (ascii and unicode whitespace other than \x20 itself) to their ampersand-hash equivalents.
function ConvertTo-AmpersandHash {
param ([Parameter(Mandatory)][String]$Value)
# there's got to be a better way of doing this.
$AMPERHASH = '&#'
$SEMICOLON = ';'
for ($i = 0x0; $i -lt 0x20; $i++) {
$value = $value -replace [char]$i,($AMPERHASH + $i + $SEMICOLON)
}
for ($i = 0x7f; $i -le 0xa0; $i++) {
$value = $value -replace [char]$i,($AMPERHASH + $i + $SEMICOLON)
}
return $Value
}
As can be seen by the embedded comment, I'm sure there's a better way to do this. As it stands, one does some 65 iterations for each incoming string. Would regular expressions work better/faster?
LATER
-replace '([\x00-\x1f\x7f-\xa0])',('&#' + [byte][char]$1 + ';')
looks promising but the $1 is evaluating to zero all the time, giving me  all the time.
LATER STILL
Thinking that -replace couldn't internally iterate, I came up with
$t = [char]0 + [char]1 + [char]2 + [char]3 + [char]4 + [char]5 + [char]6
$r = '([\x00-\x1f\x7f-\xa0])'
while ($t -match [regex]$r) {
$t = $t -replace [regex]$r, ('&#' + [byte][char]$1 + ';')
}
echo $t
However out of that I still get

FINALLY
function ConvertTo-AmpersandHash {
param ([Parameter(Mandatory)][String]$Value)
$AMPERHASH = '&#'
$SEMICOLON = ';'
$patt = '([\x00-\x1f\x7f-\xa0]{1})'
while ($Value -match [regex]$patt) {
$Value = $Value -replace $Matches[0], ($AMPERHASH + [byte][char]$Matches[0] + $SEMICOLON)
}
return $Value
}
That works better. Faster too. Any advances on that?
Kory Gill's answer with the library call is surely a better approach, but to address your regex question, you can't evaluate code in the replacement with the -replace operator.
To do that, you need to use the .Net regex replace method, and pass it a scriptblock to evaluate the replacement, which takes a parameter of the match. e.g.
PS C:\> [regex]::Replace([string][char]2,
'([\x00-\x20\x7f-\xa0])',
{param([string]$m) '&#' + [byte][char]$m + ';'})

Your question is a little unclear to me, and could be a duplicate of What is the best way to escape HTML-specific characters in a string (PowerShell)?.
It would be nice if you explicitly stated the exact string you have and what you want it to converted to. One has to read the code to try to guess.
I am guessing one or more of these functions will do what you want:
$a = "http://foo.org/bar?baz & also <value> conversion"
"a"
$a
$b = [uri]::EscapeDataString($a)
"b"
$b
$c = [uri]::UnescapeDataString($b)
"c"
$c
Add-Type -AssemblyName System.Web
$d = [System.Web.HttpUtility]::HtmlEncode($a)
"d"
$d
$e = [System.Web.HttpUtility]::HtmlDecode($d)
"e"
$e
Gives:
a
http://foo.org/bar?baz & also <value> conversion
b
http%3A%2F%2Ffoo.org%2Fbar%3Fbaz%20%26%20also%20%3Cvalue%3E%20conversion
c
http://foo.org/bar?baz & also <value> conversion
d
http://foo.org/bar?baz & also <value> conversion
e
http://foo.org/bar?baz & also <value> conversion
I have one small function which helps me replacing as per my requirement:
$SpecChars are all the characters that are going to be replaced with nothing
Function Convert-ToFriendlyName
{param ($Text)
# Unwanted characters (includes spaces and '-') converted to a regex:
$SpecChars = '\', ' ','\\'
$remspecchars = [string]::join('|', ($SpecChars | % {[regex]::escape($_)}))
# Convert the text given to correct naming format (Uppercase)
$name = (Get-Culture).textinfo.totitlecase(“$Text”.tolower())
# Remove unwanted characters
$name = $name -replace $remspecchars, ""
$name
}
Example: Convert-ToFriendlyName "My\Name\isRana\Dip " will result me "MyNameIsranaDip".
Hope it helps you.