How to find data and replace variable in PowerShell Command - powershell

I currently have a series of Dyanmic Distribution Groups that I want to edit the recipient filter on. Our company is based by location number, which is a 4 digit number. This number is part of the display name of the dynamic distribution group...example webcontact_1234_DG....1234 would be the 4 digit center number. I am wanting to replace the recipient filter to have office -eq (1234) but have it pull the number from the display name. All display names are going to be the same number of characters before the 4 digit center number, for example, webcontact_1234_DG, webcontact_2345_DG, webcontact_3456_DG, etc.
I have a replace code but it changed the office location to null.
Here is the code that I am using:
$groups=Get-DynamicDistributionGroup -filter {alias -like "webcontact*"}
foreach ($group in $groups) {
$locationcode=$($group.alias).tostring.replace("\\D", "")
set-dynamicdistributiongroup $group -recipientfilter {((((office -eq $locationcode) -and
(have the recipent filter here but can't display due to confidential information) -and
(RecipientType -eq 'UserMailbox') -and
(-not(RecipientTypeDetails -eq 'RoomMailbox')) -and
(-not(RecipientTypeDetails -eq 'SharedMailbox'))))}
}

This is the best answer I can come up with based on the given information. I assume you are having a problem getting that number out of your webcontact_1234_DG, I would use regex to get those numbers out and into another variable.
$locationcode = [regex]::Match($group.alias,'^[^_]+_([^_]+)_[^_]+$').Groups[1].Value
The above code will grab everything in between the two underscores.
Try that and let me know.

It's easiest to use the -split operator to extract the number (text) from your values[1]:
$locationcode = ($group.Alias -split '_')[1]
-split '_' returns the array of tokens that result when you split the input string by _ chars., and [1] returns the 2nd token, which is the desired location number.
Simple example:
PS> ('webcontact_3456_DG' -split '_')[1]
3456
Alternatively, a corrected version of your own attempt (see below) would use the -replace operator:
PS> 'webcontact_3456_DG' -replace '\D' # remove all non-digit chars.
3456
As for what you tried:
$($group.alias).tostring.replace("\\D", "")
The [string] type's .Replace() searches by literal strings, so the search for \\D in your names will fail, and no replacement will occur.
Additionally, note that PowerShell's escape character is ` (backtick), not \, and that \ therefore doesn't require escaping: in PowerShell "\\D" literally becomes \\D.
As an aside: There is generally no need to put $(...) around expressions.
[1] You could also use the [string] type's .Split() .NET method in this simple case, but I suggest using the far more flexible -split PS operator as a matter of habit.

Related

PowerShell to check the string in the array [duplicate]

I am trying to filter out users that are in a specific group.
I got the following output in a variable:
Group1
Group2
etc...
One group for each line saved in an array. Im trying to filter out only one specific group. But when I use -contains it always says $false, even tho the group is there.
My Code:
$group = get-aduser -identity name -properties memberof |
select-object -expandproperty memberof | %{ (get-adgroup $_).name }
$contains = $group -contains "string"
$contains is $false even if the array has elements that contain the string...
What am I missing?
It looks like your misconception was that you expected PowerShell's -contains operator to perform substring matching against the elements of the LHS array.
Instead, it performs equality tests - as -eq would - against the array's elements - see this answer for details.
In order to perform literal substring matching against the elements of an array, use:
# With non-literal search strings:
[bool] $contains = $group -match ([regex]::Escape($someString))
# With a string literal that doesn't contain regex metachars.,
# escaping isn't needed.
[bool] $contains = $group -match 'foo'
# With a string literal with metachars., you must individually \-escape them.
[bool] $contains = $group -match 'foo\.bar'
Note:
The above shows a robust, generic way of ensuring that your search string is treated as a literal value using [regex]::Escape(), which is necessary because -match expects a regex (regular expression) as its RHS (the search pattern).
Escaping isn't always necessary; specifically, only the presence of so-called metacharacters (those with special meaning in a regex, such as .) requires it, and when you're using a string literal, you can opt to directly \-escape them; e.g., to search for literal substring a.b, you can pass 'a\.b'.
Chances are that AD group names do not require escaping, but it's important to be aware of the need for it in general.
As with all operators in PowerShell, by default the matching is case-insensitive; use the -cmatch variant for case-sensitive matching.
The [bool] type constrained above is used to ensure that the result of the -match operation is converted to a Boolean:
While -match directly returns a Boolean with a scalar (non-array) LHS, with an array LHS it acts as a filter, and returns the matching array elements instead; interpreted in a Boolean context, such as in an if conditional, that usually still gives the expected result, because a non-empty array is interpreted as $true, whereas an empty one as $false; again, however it's important to know the difference.
This will rarely be a performance concern in practice, but it is worth noting that -match, due to acting as a filter with arrays, always matches against all array elements - it doesn't stop once the first match is found, the way that the -contains and -in operators do.
On the plus side, you can use -match to obtain the matching elements themselves.
The mistaken expectation of -contains performing substring matching may have arisen from confusion with the similarly named, but unrelated String.Contains() method, which indeed performs literal substring matching; e.g., 'foo'.Contains('o') yields $true.
Also note that .Contains() is case-sensitive - invariably in Windows PowerShell, by default in PowerShell (Core) 7+.
PowerShell has no operator for literal substring matching.
However, you could combine PowerShell's generic array-filtering features with the .Contains() string method - but note that this will typically perform (potentially much) worse than the -match approach.
A reasonably performant alternative is to use the PSv4+ .Where() array method as follows:
# Note: Substring search is case-sensitive here.
[bool] $contains = $group.Where({ $_.Contains("string") }, 'First')
On the plus side, this approach stops matching once the first match is found.
The answer was -match insted of contains. Now the output is true.

Stripping Data From a String In Powershell

I'm pulling the hostnames from all computers in an AD domain and the current command formats it in url form with the hostname at the end. I just need the hostnames so I'd like to strip everything to the left of the last forward slash.
(([adsi]"WinNT://$((Get-WMIObject Win32_ComputerSystem).Domain)").Children).Where({$_.schemaclassname -eq 'computer'}) | %{ $_.Path }
It's outputting as it should, I just happen to just need the hostname, so instead of WinNT://subdomain.somedomain.local/hostname I just got hostname which I would then redirect to an output file.
You can use the -Split operator to help retrieve the data:
"WinNT://subdomain.somedomain.local/hostname" -Split "/" | Select-Object -Last 1
-Split "/" separates the value into an array of substrings using / as a delimiter. You can access the resulting parts using array indexes or Select-Object. Since you want the last value, you could alternatively access [-1] index of the resulting array (("WinNT://subdomain.somedomain.local/hostname" -Split "/")[-1]).
See About Split for more information and examples.
Just posting another option, and something else that may be useful. You can also split strings by their last index, which is the last time a character appears in it. From there you can use the Substring method to select the remainder of the string.
$lio = "WinNT://subdomain.somedomain.local/hostname".LastIndexOf('/')
"WinNT://subdomain.somedomain.local/hostname".Substring($lin + 1) # +1 to not include the slash
You can see all the methods for a string here
For things like this, I would also suggest looking at the ActiveDirectory module. You can run Get-ADComputer and select specific fields really easily.

Arrays and -contains - test for substrings in the elements of an array

I am trying to filter out users that are in a specific group.
I got the following output in a variable:
Group1
Group2
etc...
One group for each line saved in an array. Im trying to filter out only one specific group. But when I use -contains it always says $false, even tho the group is there.
My Code:
$group = get-aduser -identity name -properties memberof |
select-object -expandproperty memberof | %{ (get-adgroup $_).name }
$contains = $group -contains "string"
$contains is $false even if the array has elements that contain the string...
What am I missing?
It looks like your misconception was that you expected PowerShell's -contains operator to perform substring matching against the elements of the LHS array.
Instead, it performs equality tests - as -eq would - against the array's elements - see this answer for details.
In order to perform literal substring matching against the elements of an array, use:
# With non-literal search strings:
[bool] $contains = $group -match ([regex]::Escape($someString))
# With a string literal that doesn't contain regex metachars.,
# escaping isn't needed.
[bool] $contains = $group -match 'foo'
# With a string literal with metachars., you must individually \-escape them.
[bool] $contains = $group -match 'foo\.bar'
Note:
The above shows a robust, generic way of ensuring that your search string is treated as a literal value using [regex]::Escape(), which is necessary because -match expects a regex (regular expression) as its RHS (the search pattern).
Escaping isn't always necessary; specifically, only the presence of so-called metacharacters (those with special meaning in a regex, such as .) requires it, and when you're using a string literal, you can opt to directly \-escape them; e.g., to search for literal substring a.b, you can pass 'a\.b'.
Chances are that AD group names do not require escaping, but it's important to be aware of the need for it in general.
As with all operators in PowerShell, by default the matching is case-insensitive; use the -cmatch variant for case-sensitive matching.
The [bool] type constrained above is used to ensure that the result of the -match operation is converted to a Boolean:
While -match directly returns a Boolean with a scalar (non-array) LHS, with an array LHS it acts as a filter, and returns the matching array elements instead; interpreted in a Boolean context, such as in an if conditional, that usually still gives the expected result, because a non-empty array is interpreted as $true, whereas an empty one as $false; again, however it's important to know the difference.
This will rarely be a performance concern in practice, but it is worth noting that -match, due to acting as a filter with arrays, always matches against all array elements - it doesn't stop once the first match is found, the way that the -contains and -in operators do.
On the plus side, you can use -match to obtain the matching elements themselves.
The mistaken expectation of -contains performing substring matching may have arisen from confusion with the similarly named, but unrelated String.Contains() method, which indeed performs literal substring matching; e.g., 'foo'.Contains('o') yields $true.
Also note that .Contains() is case-sensitive - invariably in Windows PowerShell, by default in PowerShell (Core) 7+.
PowerShell has no operator for literal substring matching.
However, you could combine PowerShell's generic array-filtering features with the .Contains() string method - but note that this will typically perform (potentially much) worse than the -match approach.
A reasonably performant alternative is to use the PSv4+ .Where() array method as follows:
# Note: Substring search is case-sensitive here.
[bool] $contains = $group.Where({ $_.Contains("string") }, 'First')
On the plus side, this approach stops matching once the first match is found.
The answer was -match insted of contains. Now the output is true.

Get Substring of value when using import-Csv in PowerShell

I have a PowerShell script that imports a CSV file, filters out rows from two columns and then concatenates a string and exports to a new CSV file.
Import-Csv "redirect_and_canonical_chains.csv" |
Where { $_."Number of Redirects" -gt 1} |
Select {"Redirect 301 ",$_.Address, $_."Final Address"} |
Export-Csv "testing-export.csv" –NoTypeInformation
This all works fine however for the $_.Address value I want to strip the domain, sub-domain and protocol etc using the following regex
^(?:https?:\/\/)?(?:[^#\/\n]+#)?(?:www\.)?([^:\/\n]+)
This individually works and matches as I want but I am not sure of the best way to implement when selecting the data (should I use $match, -replace etc) or whether I should do it after importing?
Any advice greatly appreciated!
Many thanks
Mike
The best place to do it would be in the select clause, as in:
select Property1,Property2,#{name='NewProperty';expression={$_.Property3 -replace '<regex>',''}}
That's what a calculated property is: you give the name, and the way to create it.Your regex might need revision to work with PowerShell, though.
I've realized now that I can just use .Replace in the following way :)
Select {"Redirect 301 ",$_.Address.Replace('http://', 'testing'), $_."Final Address"}
Based on follow-up comments, the intent behind your Select[-Object] call was to create a single string with space-separated entries from each input object.
Note that use of Export-Csv then makes no sense, because it will create a single Length column with the input strings' length rather than output the strings themselves.
In a follow-up comment you posted a solution that used Write-Host to produce the output string, but Write-Host is generally the wrong tool to use, unless the intent is explicitly to write to the display only, thereby bypassing PowerShell's output streams and thus the ability to send the output to other commands, capture it in a variable or redirect it to a file.
Here's a fixed version of your command, which uses the -join operator to join the elements of a string array to output a single, space-separated string:
$sampleCsvInput = [pscustomobject] #{
Address = 'http://www.example.org/more/stuff';
'Final Address' = 'more/stuff2'
}
$sampleCsvInput | ForEach-Object {
"Redirect 301 ",
($_.Address -replace '^(?:https?://)?(?:[^#/\n]+#)?(?:www\.)?([^:/\n]+)', ''),
$_.'Final Address' -join ' '
}
Note that , - PowerShell's array-construction operator - has higher precedence than the -join operator, so the -join operation indeed joins all 3 preceding array elements.
The above yields the following string:
Redirect 301 /more/stuff more/stuff2

What is wrong with this PowerShell -Match wildcard? [duplicate]

Reading official docs it's obvious that PowerShell -match operator is more powerful than -like (due to regular expressions). Secondly, it seems ~10 times faster according to this article https://www.pluralsight.com/blog/software-development/powershell-operators-like-match.
Are there specific cases when I should prefer -like instead of -match? If there not, why at all should I use -like? Does it exist because of historical reasons?
I've never seen -match test that much faster than -like, if at all. Normally I see -like at about the same or better speed.
But I never rely on one test instance, I usually run through about 10K reps of each.
If your're looking for performance, always prefer string methods if they'll meet the requirements:
$string = '123abc'
(measure-command {
for ($i=0;$i -lt 1e5;$i++)
{$string.contains('3ab')}
}).totalmilliseconds
(measure-command {
for ($i=0;$i -lt 1e5;$i++)
{$string -like '*3ab*'}
}).totalmilliseconds
(measure-command {
for ($i=0;$i -lt 1e5;$i++)
{$string -match '3ab'}
}).totalmilliseconds
265.3494
586.424
646.4878
See this explanation from Differences Between -Like and -Match
In a nutshell, if you are thinking, 'I am probably going to need a wildcard to find this item', then start with -Like. However, if you are pretty sure of most of the letters in the word that you are looking for, then you are better off experimenting with -Match.
Here is a more technical distinction: -Match is a regular expression, whereas -Like is just a wildcard comparison, a subset of -Match.
So, whenever you are not sure what character classes, i.e. digits, letters, punctuation, etc., can there be inside, when you just want to match any character, you should be using -Like with its wildcards.
When you know there must be a digit at the start followed with 1+ sequences of a colon followed with alphanumeric characters up to the end of the string, use -Match with its powerful regular expressions.
You should prefer -like when your comparator string is a dos-style filename wildcard. If you have a cmdlet that is designed to look like a "standard" windows command line application, then you can expect file name parameters to include dos-style wildcards.
You might have a grep-like cmdlet that takes a regex and a list of files. I can imagine it being used like this:
> yourMagicGrepper "^Pa(tt).*rn" *.txt file.*
You would use -match when working with the first parameter, and -like for all other parameters.
In other words: it depends on your functional requirements.