Remove All Values In A String Before 7th backslash - powershell

I have a path inside a txt file that needs to be manipulated, something like:
C:\Jenkins\Automation\Blah\Foo\Bar\646433\Schema\test\473289_12321.ps1
C:\Jenkins\Automation\Blah\Foo\Bar\2112\Schema\QA\473289_123211.ps1
I want to replace everything before the 7th backslash and then replace it with C:\Question. I was doing something similar in Powershell via:
(Get-Content $FullEnvSQLFilePath) | #get the content
Foreach-Object {$_ -replace [Regex]::Escape($StringToReplace), "$StringReplaceValue"} | #look for the string and replace
This worked fine when I knew what the exact verbiage was to look for. We now don't know that but we will want to remove everything before the 7th backslash and replace it with a value. Reverse order works fine too. I wasn't able to have much luck in Powershell via substring doing this. Thanks.

One option:
$text =
'C:\Jenkins\Automation\Blah\Foo\Bar\646433\Schema\test\473289_12321.ps1',
'C:\Jenkins\Automation\Blah\Foo\Bar\2112\Schema\QA\473289_123211.ps1'
$text | foreach {'C:\Question\{0}' -f $_.split('\',8)[-1]}
C:\Question\Schema\test\473289_12321.ps1
C:\Question\Schema\QA\473289_123211.ps1

this ([^\\]*\\){7} regex looks 7 times for a capture group ending on a backslash and replaces it.
UPDATED: .:\\([^\\]*\\){6} regex looks for strings that look like paths starting at any root drive .:\ followed by 6 times a capture group ending on a backslash based on your comment
$text = #"
C:\Jenkins\Automation\Blah\Foo\Bar\646433\Schema\test\473289_12321.ps1
C:\Jenkins\Automation\Blah\Foo\Bar\2112\Schema\QA\473289_123211.ps1
PRINT C:\Jenkins\Automation\Blah\Foo\Baz\2112\Schema\QA\473289_123212.ps1
C:\Jenkins\Automation\Blah\Foo\quux\2112\Schema\QA\473289_123213.ps1
"#
#depending on how you get the text (single string or array)
#$text.Split("`n") | % { $_ -Replace '.:\\([^\\]*\\){6}','C:\Example\' }
$text -Replace ".:\\([^\\`n]*\\){6}","C:\Example\"
Result:
C:\Example\Schema\test\473289_12321.ps1
C:\Example\Schema\QA\473289_123211.ps1
PRINT C:\Example\Schema\QA\473289_123212.ps1
C:\Example\Schema\QA\473289_123213.ps1

Related

Powershell replace last two occurrences of a '/' in file path with '.'

I have a filepath, and I'm trying to remove the last two occurrences of the / character into . and also completely remove the '{}' via Powershell to then turn that into a variable.
So, turn this:
xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx
Into this:
xxx-xxx-xx\xxxxxxx\x\xxxx-xxxxx-xxxx.xxxxx.xxxxx
I've tried to get this working with the replace cmdlet, but this seems to focus more on replacing all occurrences or the first/last occurrence, which isn't my issue. Any guidance would be appreciated!
Edit:
So, I have an excel file and i'm creating a powershell script that uses a for each loop over every row, which amounts to thousands of entries. For each of those entries, I want to create a secondary variable that will take the full path, and save that path minus the last two slashes. Here's the portion of the script that i'm working on:
Foreach($script in $roboSource)
{
$logFileName = "$($script.a).txt".Replace('(?<=^[^\]+-[^\]+)-','.')
}
$script.a will output thousands of entries in this format:
xxx-xxx-xx\xxxxxxx\x{xxxx-xxxxx-xxxx}\xxxxx\xxxxx
Which is expected.
I want $logFileName to output this:
xxx-xxx-xx\xxxxxxx\x\xxxx-xxxxx-xxxx.xxxxx.xxxxx
I'm just starting to understand regex, and I believe the capture group between the parenthesis should be catching at least one of the '\', but testing attempts show no changes after adding the replace+regex.
Please let me know if I can provide more info.
Thanks!
You can do this in two fairly simply -replace operations:
Remove { and }
Replace the last two \:
$str = 'xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
$str -replace '[{}]' -replace '\\([^\\]*)\\([^\\]*)$','.$1.$2'
The second pattern matches:
\\ # 1 literal '\'
( # open first capture group
[^\\]* # 0 or more non-'\' characters
) # close first capture group
\\ # 1 literal '\'
( # open second capture group
[^\\]* # 0 or more non-'\' characters
) # close second capture group
$ # end of string
Which we replace with the first and second capture group values, but with . before, instead of \: .$1.$2
If you're using PowerShell Core version 6.1 or newer, you can also take advantage of right-to-left -split:
($str -replace '[{}]' -split '\\',-3) -join '.'
-split '\\',-3 has the same effect as -split '\\',3, but splitting from the right rather than the left.
A 2-step approach is simplest in this case:
# Input string.
$str = 'xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
# Get everything before the "{"
$prefix = $str -replace '\{.+'
# Get everything starting with the "{", remove "{ and "}",
# and replace "\" with "."
$suffix = $str.Substring($prefix.Length) -replace '[{}]' -replace '\\', '.'
# Output the combined result (or assign to $logFileName)
$prefix + $suffix
If you wanted to do it with a single -replace operation (with nesting), things get more complicated:
Note: This solution requires PowerShell Core (v6.1+)
$str -replace '(.+)\{(.+)\}(.+)',
{ $_.Groups[1].Value + $_.Groups[2].Value + ($_.Groups[3].Value -replace '\\', '.') }
Also see the elegant PS-Core-only -split based solution with a negative index (to split only a fixed number of tokens off the end) in Mathias R. Jessen's helpful answer.
try this
$str='xxx-xxx-xx\xxxxxxx\x\{xxxx-xxxxx-xxxx}\xxxxx\xxxxx'
#remove bracket and split for get array
$Array=$str -replace '[{}]' -split '\\'
#take all element except 2 last elements, and concat after last elems
"{0}.{1}.{2}" -f ($Array[0..($Array.Length -3)] -join '\'), $Array[-2], $Array[-1]

How can I replace every comma with a space in a text file before a pattern using PowerShell

I have a text file with lines in this format:
FirstName,LastName,SSN,$x.xx,$x.xx,$x.xx
FirstName,MiddleInitial,LastName,SSN,$x.xx,$x.xx,$x.xx
The lines could be in either format. For example:
Joe,Smith,123-45-6789,$150.00,$150.00,$0.00
Jane,F,Doe,987-65-4321,$250.00,$500.00,$0.00
I want to basically turn everything before the SSN into a single field for the name thus:
Joe Smith,123-45-6789,$150.00,$150.00,$0.00
Jane F Doe,987-65-4321,$250.00,$500.00,$0.00
How can I do this using PowerShell? I think I need to use ForEach-Object and at some point replace "," with " ", but I don't know how to specify the pattern. I also don't know how to use a ForEach-Object with a $_.Where so that I can specify the "SkipUntil" mode.
Thanks very much!
Mathias is correct; you want to use the -replace operator, which uses regular expressions. I think this will do what you want:
$string -replace ',(?=.*,\d{3}-\d{2}-\d{4})',' '
The regular expression uses a lookahead (?=) to look for any commas that are followed by any number of any character (. is any character, * is any number of them including 0) that are then followed by a comma immediately followed by a SSN (\d{3}-\d{2}-\d{4}). The concept of "zero-width assertions", such as this lookahead, simply means that it is used to determine the match, but it not actually returned as part of the match.
That's how we're able to match only the commas in the names themselves, and then replace them with a space.
I know it's answered, and neatly so, but I tried to come up with an alternative to using a regex - count the number of commas in a line, then replace either the first one, or the first two, commas in the line.
But strings can't count how many times a character appears in them without using the regex engine(*), and replacements can't be done a specific number of times without using the regex engine(**), so it's not very neat:
$comma = [regex]","
Get-Content data.csv | ForEach {
$numOfCommasToReplace = $comma.Matches($_).Count - 4
$comma.Replace($_, ' ', $numOfCommasToReplace)
} | Out-File data2.csv
Avoiding the regex engine entirely, just for fun, gets me things like this:
Get-Content .\data.csv | ForEach {
$1,$2,$3,$4,$5,$6,$7 = $_ -split ','
if ($7) {"$1 $2 $3,$4,$5,$6,$7"} else {"$1 $2,$3,$4,$5,$6"}
} | Out-File data2.csv
(*) ($line -as [char[]] -eq ',').Count
(**) while ( #counting ) { # split/mangle/join }

How to Split DistinguishedName?

I have a list of folks and their DN from AD (I do not have direct access to that AD). Their DNs are in format:
$DNList = 'CN=Bob Dylan,OU=Users,OU=Dept,OU=Agency,OU=NorthState,DC=myworld,DC=com',
'CN=Ray Charles,OU=Contractors,OU=Dept,OU=Agency,OU=NorthState,DC=myworld,DC=com',
'CN=Martin Sheen,OU=Users,OU=Dept,OU=Agency,OU=WaySouth,DC=myworld,DC=com'
I'd like to make $DNList return the following:
OU=Users,OU=Dept,OU=Agency,OU=NorthState,DC=myworld,DC=com
OU=Contractors,OU=Dept,OU=Agency,OU=NorthState,DC=myworld,DC=com
OU=Users,OU=Dept,OU=Agency,OU=WaySouth,DC=myworld,DC=com
I decided to turn my comment into an answer:
$DNList | ForEach-Object {
$_ -replace '^.+?(?<!\\),',''
}
Debuggex Demo
This will correctly handle escaped commas that are part of the first component.
We do a non-greedy match for one or more characters at the beginning of the string, then look for a comma that is not preceded by a backslash (so that the dot will match the backslash and comma combination and keep going).
You can remove the first element with a replacement like this:
$DNList -replace '^.*?,(..=.*)$', '$1'
^.*?, is the shortest match from the beginning of the string to a comma.
(..=.*)$ matches the rest of the string (starting with two characters after the comma followed by a = character) and groups them, so that the match can be referenced in the replacement as $1.
You have 7 items per user, comma separated and you want rid of the first one.
So, split each item in the array using commas as the delimiter, return matches 1-6 (0 being the first item that you want to skip), then join with commas again e.g.
$DNList = $DNList|foreach{($_ -split ',')[1..6] -join ','}
If you then enter $DNList it returns
OU=Users,OU=Dept,OU=Agency,OU=NorthState,DC=myworld,DC=com
OU=Contractors,OU=Dept,OU=Agency,OU=NorthState,DC=myworld,DC=com
OU=Users,OU=Dept,OU=Agency,OU=WaySouth,DC=myworld,DC=com
Similar to Grahams answer but removed the hardcoded array values so it will just remove the CN portion without worrying how long the DN is.
$DNList | ForEach-Object{($_ -split "," | Select-Object -Skip 1) -join ","}
Ansgar most likely has a good reason but you can just use regex to remove every before the first comma
$DNList -replace "^.*?,"
Update based on briantist
To maintain a different answer but one that works this regex can still have issues but I doubt these characters will appear in a username
$DNList -replace "^.*?,(?=OU=)"
Regex uses a look ahead to be sure the , is followed by OU=
Similarly you could do this
($DNList | ForEach-Object{($_ -split "(,OU=)" | Select-Object -Skip 1) -join ""}) -replace "^,"

Parsing imported CSV

I am trying to parse a .csv file using the Import-CSV commandlet as shown below:
$lease_import = ipcsv -Path 'C:\leases.csv'-Delimiter "," | where {
($_.binding_state -ne "ABANDONED") -and ($_.mac_address -ne "") -and (($_.ends -replace "T"," " )-match "2014")
}
A general 'ends' field in this CSV looks like this: 2014-01-23T13:49:38.000Z. I want to replace the 'T' with a space and I want to replace .000Z with an empty string. Finally, the dashes would have to get replaced by a /. End result would have to be something along the lines of this:
2014/01/23 13:49:38
I tried starting simple by using -replace to change the "T" to a space following this link: http://blogs.technet.com/b/heyscriptingguy/archive/2011/03/21/use-powershell-to-replace-text-in-strings.aspx. However, this doesn't work for me.
Am I trying to do too much at once?
There could be more elegant solutions by trying to parse the DateTime out of the string but a very simple one would be
("2014-01-23T13:49:38.000Z".TrimEnd(".000Z") -Replace "T"," ") -replace "-","/"
which would return
2014/01/23 13:49:38
In your loop this would look like
($_.ends.TrimEnd(".000Z") -Replace "T"," ") -replace "-","/"
In place of .TrimEnd() you could just do another Replace but since TrimEnd is there to remove trailing characters why not use it.
I did try to use something like [datetime]::ParseExact but they need to have clean strings with not extra data else you get parsing errors. You can clean the string like we have with the -replace's but then it becomes redundant to use the function. Someone else might have a cleaner approach

Change specific part of a string

I've got a .txt-File with some text in it:
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
PKG_NAME;"WinBasics"
PKG_RELATED_TICKET;""
PKG_CUSTOMER_DNS_SERVERS;"12314.1231
PKG_CUSTOMER_SEARCH_DOMAINS;"ms.com"
PKG_JOIN_EXISTING_DOMAIN;"True"
PKG_DOMAINJOIN_DOMAIN;"ms.com"
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"*******"
So now, is there a way to replace those *'s with e.g. numbers or sth. ?
If so, may you tell me how to do it?
Much like Rahul I would use RegEx as well. Considering the application I'd run Get-Content through a ForEach loop, and replace text as needed on a line-by-line basis.
Get-Content C:\Path\To\File.txt | ForEach{$_ -replace "(PKG_DOMAINJOIN_PASSWD;`")([^`"]+?)(`")", "`${1}12345678`$3"}
That would output:
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
PKG_NAME;"WinBasics"
PKG_RELATED_TICKET;""
PKG_CUSTOMER_DNS_SERVERS;"12314.1231
PKG_CUSTOMER_SEARCH_DOMAINS;"ms.com"
PKG_JOIN_EXISTING_DOMAIN;"True"
PKG_DOMAINJOIN_DOMAIN;"ms.com"
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"12345678"
On second thought, I don't know if I'd do that. I might import it as a CSV, update the property, and export the CSV again.
Import-CSV C:\Path\To\File.txt -Delimiter ";" |%{if($_.Property -eq "PKG_DOMAINJOIN_PASSWD"){$_.value = "12345678";$_}else{$_}|export-csv c:\path\to\newfile.txt -delimiter ";" -notype
If You are using Powershell V2.0 (Hopefully) you can try something like below. gc is short hand for get-content commandlet.
(gc D:\SO_Test\test.txt) -replace '\*+','12345678'
With this the resultant data would be as below (notice the last line)
Property;Value
PKG_GUID;"939de9ec-c9ac-4e03-8bef-7b7ab99bff74"
<Rest of the lines here>
PKG_DOMAINJOIN_USER;"mdoe"
PKG_DOMAINJOIN_PASSWD;"12345678" <-- Notice here; *'s changed to numbers
Rahul's answer was good, I just wanted to mention that *+ will replace all instances of a single * character or more, so it would match any other place there is at least one star. If what you posted is all you would ever expect for you sample data though this would be fine.
You could alter the regex match to make it more specific if it was needed by changing it to something like
\*{3,0}
which would match 3 or more stars, or very specific would be
(?<=")\*{3,}(?=")
which would replace 3 or more stars which are surrounded by double quotes.
Here's a function that uses regex lookahead and lookbehind zero-length assertions to replace named parameters in a string similar to your example:
function replace-x( $string, $name, $value ) {
$regex = "(?<=$([regex]::Escape($name));`").*(?=`")"
$string -replace $regex, $value
}
Its reusable for different settings in your file, e.g:
$settings = get-content $filename
$settings = replace-x $settings PKG_DOMAINJOIN_USER foo
$settings = replace-x $settings PKG_DOMAINJOIN_PASSWD bar