I have been trying to transform the following string:
CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC=FR,DC=CONTOSO,DC=COM
to:
FR.CONTOSO.COM
As a first step, I tried to remove everything until ",DC" pattern.
I thought I could use the un-greedy ".+?" to match everything until the first ",DC" pattern:
$str = 'CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC=FR,DC=CONTOSO,DC=COM'
$str -replace '.+?,DC', ''
This returns:
=COM
Any idea why it's getting only the last one even with the un-greedy version?
How could I do this?
Just split the string:
$parts = ('CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC=FR,DC=CONTOSO,DC=COM' -split ',')
$newString = '{0}.{1}.{2}' -f $parts[-3].split('=')[1], $parts[-2].split('=')[1], $parts[-1].split('=')[1]
OK, so I took #EBGreen's advice and decided to make a function that does all the work:
#Requires -Version 4
Function ConvertFrom-DistinguishedName
{
[CmdletBinding()]
[OutputType('System.Management.Automation.PSCustomObject')]
Param(
[Parameter(Position = 0, Mandatory)]
[Alias('DistinguishedName', 'Name')]
[ValidatePattern('^CN=.+?,(OU=.+?,)+DC=.+$')]
[string]
$Path
)
$local:ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
$WS = $Path -split ',' # working set
$User = $WS[0] -replace 'CN='
$Domain = $WS.Where({$PSItem.StartsWith('DC=')}).ToLower() -replace 'dc=' -join '.'
$OU = $WS.Where({$PSItem.StartsWith('OU=')}) -replace 'OU='
[array]::Reverse($OU)
[pscustomobject]#{
'User' = $User
'Domain' = $Domain
'OU' = $OU -join '\'
}
}
PS C:\> ConvertFrom-DistinguishedName 'CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC=FR,DC=CONTOSO,DC=COM'
User Domain OU
---- ------ --
John Doe fr.contoso.com HQ\Department\Support\IT
The end result? This converts your distinguished AD name to a PSCustomObject you can easily work with.
To get the domain out of the Distinguishedname, you will want to use -Split. Finally we can use -join to insert '.' between the DCs
This will store your domain string to $domain:
$dn = "CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC=FR,DC=CONTOSO,DC=COM"
$domain = $dn -Split "," | ? {$_ -like "DC=*"}
$domain = $domain -join "." -replace ("DC=", "")
Write-Host "Current Domain: " $domain
You should use the ^ to start from the beginning.
$str = 'CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC=FR,DC=CONTOSO,DC=COM'
$str = $str -replace '^.+?,DC',''
$str = $str -replace 'DC=',''
$str -replace ',','.'
Since nobody bothered explaining the behavior yet:
The replacement operation replaces every occurrence of the pattern .+?,DC in the string, so it first removes CN=John Doe,OU=IT,OU=Support,OU=Department,OU=HQ,DC, then continues where that replacement left off and removes =FR,DC, and then =CONTOSO,DC, leaving you with just =COM.
To avoid this behavior you need to anchor the expression at the beginning of the string (^), as others have already suggested. The second replacement for substituting the remaining ,DC= with dots can be daisy-chained to the first one, so you need just one statement:
$str -replace '^.*?,dc=' -replace ',dc=', '.'
Related
this is what i got so far
if the command places a forth line of output then the regex shouldn't be match, but it does for me.
any ideas?
$From = "cody#tech.com"
$To = "cody#tech.com"
$Subject = "hello"
$Body = "body"
$SMTPServer = "smtp.office365.com"
$SMTPPort = "587"
$Attachment = "c:\checkpointlog.txt"
$RE = [regex]"(?smi)^Checkpointing to checkpoint.*^MD5 \(checkpoint.*^Rotating D:\\Perforce\\Server\\journal to journal.*?$"
$output = p4d -jc
$output | add-content c:\checkpointlog.txt
if ($output -notmatch $RE) {
$param = #{
From = $From
To = $To
Subject = $Subject
Body = $Body
SmtpServer = $SMTPServer
port = $SMTPPort
Usessl = $True
Credential = $cred
Attachments = $Attachment
}
Send-MailMessage #param
Write-Host 'unexpected value, email sent'
exit
}
else {
Write-Host 'continuing script'
}
the output of a Perforce Helix Server command p4 -jc should always be like the following 3 lines:
Checkpointing to checkpoint.22...
MD5 (checkpoint.22) = F561234wer2B8E5123456767745645616D
Rotating D:\Perforce\Server\journal to journal.21...
I would like to use a regex in an if statement so that if the output doesn't match the 3 line string below then an email is sent with the log file for us to inspect.
Checkpointing to checkpoint.*
MD5 (checkpoint.*
Rotating D:\Perforce\Server\journal to journal.*
I am hoping to use a wild card * to account for the incremental number
Any ideas would be great!
Your regex tries to match multiple lines, and therefore needs a single multi-line string as input.
Capturing an external program's output returns an array of strings (lines).
Using an array of string as the LHS of the -match operator causes PowerShell to match the regex against each string individually.
Therefore, join the lines output by p4d with newlines to form a single multi-line string, so that your regex can match multiple lines:
$output = (p4d -jc) -join "`n"
Additionally, if you want to make sure that your regex matches the entire input, not just a substring, restructure you regex as follows:
Remove in-line option m (multi-line) so that ^ and $ truly only match the very start and end of the multi-line string
Remove in-line option s, so that . doesn't match newlines, and match newlines explicitly with \n (instead of ^).
$RE = '(?i)^Checkpointing to checkpoint.*\nMD5 \(checkpoint.*\nRotating D:\\Perforce\\Server\\journal to journal.*$'
Build (and test) your Regular Expression on a website like regex101.com
I suggest to use splatting
$RE = [regex]"(?smi)^Checkpointing to checkpoint.*^MD5 \(checkpoint.*^Rotating D:\\Perforce\\Server\\journal to journal.*?$"
$output = p4d -jc
$output | add-content c:\checkpointlog.txt
if ($output -notmatch $RE) {
$param = #{
From = $From
To = $To
Subject = $Subject
Body = $Body
SmtpServer = $SMTPServer
port = $SMTPPort
Usessl = $True
Credential = $cred
Attachments = 'c:\checkpointlog.txt'
}
Send-MailMessage #param
exit
}
I know you wanted to use RegEx but maybe this would help? It might not be the greatest but it should work.
$file='checkpointlog.txt'
$line1Expected="Checkpointing to checkpoint*"
$line2Expected="MD5 (checkpoint*"
$line3Expected="Rotating D:\Perforce\Server\journal to journal*"
$line1Actual=Get-Content($file) | Select -Index 0
$line2Actual=Get-Content($file) | Select -Index 1
$line3Actual=Get-Content($file) | Select -Index 2
if($line1Actual -like $line1Expected`
-and $line2Actual -like $line2Expected`
-and $line3Actual -like $line3Expected){
}else{
echo 'send mail'
}
I have a PowerShell Scriptline that replaces(deletes) characters between the second and third underscore with an "_":
get-childitem *.pdf | rename-item -newname { $_.name -replace '_\p{L}+, \p{L}+_', "_"}
Examples:
12345_00001_LastName, FirstName_09_2018_Text_MoreText.pdf
12345_00002_LastName, FirstName-SecondName_09_2018_Text_MoreText.pdf
12345_00003_LastName, FirstName SecondName_09_2018_Text_MoreText.pdf
This _\p{L}+, \p{L}+_ regex only works for the first example. To replace everything inbetween I have used _(?:[^_]*)_([^_]*)_ (according to regex101 this should almost work) but the output is:
12345_09_MoreText.pdf
The desired output would be:
12345_00001_09_2018_Text_MoreText.pdf
12345_00002_09_2018_Text_MoreText.pdf
12345_00003_09_2018_Text_MoreText.pdf
How do I correctly replace the second and third underscore and everything inbetween with an "_"?
If you don't want to use regex -
$files = get-childitem *.pdf #get all pdf files
$ModifiedFiles, $New = #() #declaring two arrays
foreach($file in $files)
{
$ModifiedFiles = $file.split("_")
$ModifiedFiles = $ModifiedFiles | Where-Object { $_ -ne $ModifiedFiles[2] } #ommitting anything between second and third underscore
$New = "$ModifiedFiles" -replace (" ", "_")
Rename-Item -Path $file.FullName -NewName $New
}
Sample Data -
$files = "12345_00001_LastName, FirstName_09_2018_Text_MoreText.pdf", "12345_00002_LastName, FirstName-SecondName_09_2018_Text_MoreText.pdf", "12345_00003_LastName, FirstName SecondName_09_2018_Text_MoreText.pdf"
$ModifiedFiles, $New = #() #declaring two arrays
foreach($file in $files)
{
$ModifiedFiles = $file.split("_")
$ModifiedFiles = $ModifiedFiles | Where-Object { $_ -ne $ModifiedFiles[2] } #ommitting anything between second and third underscore
$New = "$ModifiedFiles" -replace (" ", "_")
}
You may use
-replace '^((?:[^_]*_){2})[^_]+_', '$1'
See the regex demo
Details
^ - start of the line
((?:[^_]*_){2}) - Group 1 (the value will be referenced to with $1 from the replacement pattern): two repetitions of
[^_]* - 0+ chars other than an underscore
_ - an underscore
[^_]+ - 1 or more chars other than _
_ - an underscore
To offer an alternative solution that avoids a complex regex: The following is based on the -split and -join operators and shows PowerShell's flexibility with respect to array slicing:
Get-ChildItem *.pdf | Rename-Item { ($_.Name -split '_')[0..1 + 3..6] -join '_' } -WhatIf
$_.Name -split '_' splits the filename by _ into an array of tokens (substrings).
Array slice [0..1 + 3..6] combines two range expressions (..) to essentially remove the token with index 2 from the array.
-join '_' reassembles the modified array into a _-separated string, yielding the desired result.
Note: 6, the upper array bound, is hard-coded above, which is suboptimal, but sufficient with input as predictable as in this case.
As of Windows PowerShell v5.1 / PowerShell Core 6.1.0, in order to determine the upper bound dynamically, you require the help of an auxiliary variable, which is clumsy:
Get-ChildItem *.pdf |
Rename-Item { ($arr = $_.Name -split '_')[0..1 + 3..($arr.Count-1)] -join '_' } -WhatIf
Wouldn't it be nice if we could write [0..1 + 3..] instead?
This and other improvements to PowerShell's slicing syntax are the subject of this feature suggestion on GitHub.
here's one other way ... using string methods.
'12345_00003_LastName, FirstName SecondName_09_2018_Text_MoreText.pdf'.
Split('_').
Where({
$_ -notmatch ','
}) -join '_'
result = 12345_00003_09_2018_Text_MoreText.pdf
that does the following ...
split on the underscores
toss out any item that has a comma in it
join the remaining items back into a string with underscores
i suspect that the pure regex solution will be faster, but you may want to use this simply to have something that is easier to understand when you next need to modify it. [grin]
I am working on a powershell script and I've got several text files where I need to replace backslashes in lines which matches this pattern: .. >\\%name% .. < .. (.. could be anything)
Example string from one of the files where the backslashes should match:
<Tag>\\%name%\TST$\Program\1.0\000\Program.msi</Tag>
Example string from one of the files where the backslashes should not match:
<Tag>/i /L*V "%TST%\filename.log" /quiet /norestart</Tag>
So far I've managed to select every char between >\\%name% and < with this expression (Regex101):
(?<=>\\\\%name%)(.*)(?=<)
but I failed to select only the backslashes.
Is there a solution which I could not yet find?
I'd recommend selecting the relevant tags with an XPath expression and then do the replacement on the text body of the selected nodes.
$xml.SelectNodes('//Tag[substring(., 1, 8) = "\\%name%"]' | ForEach-Object {
$_.'#text' = $_.'#text' -replace '\\', '\\'
}
So here's my solution:
$original_file = $Filepath
$destination_file = $Filepath + ".new"
Get-Content -Path $original_file | ForEach-Object {
$line = $_
if ($line -match '(?<=>\\\\%name%)(.*)(?=<)'){
$line = $line -replace '\\','/'
}
$line
} | Set-Content -Path $destination_file
Remove-Item $original_file
Rename-Item $destination_file.ToString() $original_file.ToString()
So this will replace every \ with an / in the given pattern but not in the way which my question was about.
Is there any easy way to do this?
input: 123215-85_01_test
expected output: 01_test
Another example
input: 12154_02_test
expected output: 02_test
There will be always string "test", but different numbering before
for example this code..
$path = "c:\tmp\*.sql"
get-childitem $path | forEach-object {
$name = $_.Name
$result = $name -replace "","" # I don't know how write this regex..
$extension = $_.Extension
$newName = $prefix+"_"+ $result -f, $extension
Rename-Item -Path $_.FullName -NewName $newName
}
There are two ways you go go at this. Simple split and join or you can use one of many regexes....
Split on underscore and rejoin last 2 elements
$split = "123215-85_01_test" -split "_"
$split[-2..-1] -join "_" # $split[-2,-1] would also work.
Regex to locate the data between the last underscores
"123215-85_01_test" -replace "^.*_(\d+)_(.*)$", '$1_$2'
Note this fails if there is more than 2 underscores.
I'm trying to replace part of a string in Powershell. However, the replacement string is not hardcoded, it's calculated from a function:
$text = "the image is -12345-"
$text = $text -replace "-(\d*)-", 'This is the image: $1'
Write-Host $text
This gives me the correct result:
"This is the image: 12345"
Now, I want to include the base64 encoded image. I can read the image from the id. I was hoping the following would work, but it doesn't:
function Get-Base64($path)
{
[convert]::ToBase64String((get-content $path -encoding byte))
}
$text -replace "-(\d*)-", "This is the image: $(Get-Base64 '$1')"
The reason that it doesn't work, is because it first passes $1 (the string, not the value of $1) to the function, executes it and only then does it do the replace. What I want to do is
Find the occurrence of the pattern
Replace each occurence with the pattern
For each replace:
Pass the capture group to the function
Use the value of the capture group to get the base64 image
inject the base64 image into the replacement
You can use the static Replace method from the [regex] class:
[regex]::Replace($text,'-(\d*)-',{param($match) "This is the image: $(Get-Base64 $match.Groups[1].Value)"})
Alternatively you can define a regex object and use the Replace method of that object:
$re = [regex]'-(\d*)-'
$re.Replace($text, {param($match) "This is the image: $(Get-Base64 $match.Groups[1].Value)"})
For better readability you could define the callback function (the scriptblock) in a separate variable and use that in the replacement:
$callback = {
param($match)
'This is the image: ' + (Get-Base64 $match.Groups[1].Value)
}
$re = [regex]'-(\d*)-'
$re.Replace($text, $callback)
PetSerAl's helpful answer is your only option in Windows PowerShell, as of v5.1.
PowerShell Core v6.1+ now offers a native PowerShell solution via an enhancement to the
-replace operator, which obviates the need to call [regex]::Replace():
Just as with [regex]::Replace(), you can now:
pass a script block as the -replace replacement operand, which must return the replacement string,
except that the match at hand (an instance of type [System.Text.RegularExpressions.Match]) is represented as automatic variable $_, as is customary in PowerShell.
Applied to your case:
$text -replace "-(\d*)-", { "This is the image: $(Get-Base64 $_.Groups[1].Value)" }
A simpler example:
# Increment the number embedded in a string:
PS> '42 years old' -replace '\d+', { [int] $_.Value + 1 }
43 years old
Here's another way. Use the -match operator, and then reference $matches. Note that $matches doesn't get set with arrays on the left side of the -match operator. $matches.1 is the first grouping formed by the ( ).
$text = "the image is -12345-"
function Get-Base64($path) {
[convert]::ToBase64String( (get-content $path -asbytestream) ) } # ps 6 ver
if (! (test-path 12345)) { echo hi > 12345 }
$text -match '-(\d*)-'
$text -replace '-(\d*)-', "$(Get-Base64 $matches.1)"
the image is aGkNCg==
Or to break it up even more:
$text -match '-(\d*)-'
$result = Get-Base64 $matches.1
$text -replace '-(\d*)-', $result