I'm trying to setup a validation check against an array. I have the following
$ValidDomain = "*.com","*.co.uk"
$ForwardDomain = Read-Host "What domain do you want to forward to? e.g. contoso.com"
#while (!($ForwardDomain -contains $ValidDomain)) {
while (!($ValidDomain.Contains($ForwardDomain))) {
Write-Warning "$ForwardDomain isn't a valid domain name format. Please try again."
$ForwardDomain = Read-Host "What domain do you want to forward to? e.g. contoso.com"
}
The commented while line is just an alternative way I've been testing this.
If I enter, when prompted by Read-Host, "fjdkjfl.com" this displays the warning message rather than saying it's valid and keeps looping.
I have tried using -match instead of -contains but get the message:
parsing "*com *co.uk" - Quantifier {x,y} following nothing.
Have I got this completely wrong?
Contains() and -contains don't work the way you expect. Use a regular expression instead:
$ValidDomain = '\.(com|co\.uk)$'
$ForwardDomain = Read-Host ...
while ($ForwardDomain -notmatch $ValidDomain) {
...
}
You can construct $ValidDomain from a list of strings like this:
$domains = 'com', 'co.uk'
$ValidDomains = '\.({0})$' -f (($domains | ForEach-Object {[regex]::Escape($_)}) -join '|')
Regular expression breakdown:
.: The dot is a special character that matches any character except newlines. To match a literal dot you need the escape sequence \..
(...): Parentheses define a (capturing) group or subexpression.
|: The pipe defines an alternation (basically an "OR"). Alternations are typically put in grouping constructs to distinguish the alternation from the rest of the expression.
$: The dollar sign is a special character that matches the end of a string.
The {0} in the format string for the -f operator is not part of the regular expression, but a placeholder that defines where (and optionally how) the second argument of the operator is inserted into the format string.
Related
Lets say I have switch statement like so:
$NewName = "test.psd"
switch -RegEX ($NewName) {
"^\..*" { #if string starts with "." it means only change extension
'Entry Starts with a "."'
}
".*\..*" { # "." is in the middle , change both basename and extension
'Entry does not start with a "."'
}
"[^.]" { # if no "." at all, it means only change base name
'No "." present'
}
}
The first and second condidtions work as expected, but the last one always triggers. It will trigger against:
$NewName = "test.psd"
$NewName = ".psd"
$NewName = "test"
Doesnt regex "[^.]" mean if there is a dot, dont match. Essentially only trigger in the absence of a dot.
My expected outcome is for the last statement to only trigger if there is not dot present.
Any help on this, would be wellcome.
That would only work if "." were the only character. All the other characters would match it. You would have to repeat that pattern for every character on the line. See also Regex - Does not contain certain Characters
'a.' -match '^[^.]+$'
False
'ab' -match '^[^.]+$'
True
The dot is a special character in regular expressions, and needs to be escaped when you want to use a literal dot. Try "[^\.]" for the regular expression in the third case.
Been scratching my head on this one...
I'd like to remove .com and capitalize S and T from: "sometext.com"
So output would be Some Text
Thank you in advance
For most of this you can use the replace() member of the String object.
The syntax is:
$string = $string.replace('what you want replaced', 'what you will replace it with')
Replace can be used to erase things by using blank quotes '' for the second argument. That's how you can get rid of .com
$string = $string.replace('.com','')
It can also be used to insert things. You can insert a space between some and text like this:
$string = $string.replace('et', 'e t')
Note that using replace does NOT change the original variable. The command below will print "that" to your screen, but the value of $string will still be "this"
$string = 'this'
$string.replace('this', 'that')
You have to set the variable to the new value with =
$string = "this"
$string = $string.replace("this", "that")
This command will change the value of $string to that.
The tricky part here comes in changing the first t to capital T without changing the last t. With strings, replace() replaces every instance of the text.
$string = "text"
$string = $string.replace('t', 'T')
This will set $string to TexT. To get around this, you can use Regex. Regex is a complex topic. Here just know that Regex objects look like strings, but their replace method works a little differently. You can add a number as a third argument to specify how many items to replace
$string = "aaaaaa"
[Regex]$reggie = 'a'
$string = $reggie.replace($string,'a',3)
This code sets $string to AAAaaa.
So here's the final code to change sometext.com to Some Text.
$string = 'sometext.com'
#Use replace() to remove text.
$string = $string.Replace('.com','')
#Use replace() to change text
$string = $string.Replace('s','S')
#Use replace() to insert text.
$string = $string.Replace('et', 'e t')
#Use a Regex object to replace the first instance of a string.
[regex]$pattern = 't'
$string = $pattern.Replace($string, 'T', 1)
What you're trying to achieve isn't well-defined, but here's a concise PowerShell Core solution:
PsCore> 'sometext.com' -replace '\.com$' -replace '^s|t(?!$)', { $_.Value.ToUpper() }
SomeText
-replace '\.com$' removes a literal trailing .com from your input string.
-replace '^s|t(?!$), { ... } matches an s char. at the start (^), and a t that is not (!) at the end ($); (?!...) is a so-called negative look-ahead assertion that looks ahead in the input string without including what it finds in the overall match.
Script block { $_.Value.ToUpper() } is called for each match, and converts the match to uppercase.
-replace (a.k.a -ireplace) is case-INsensitive by default; use -creplace for case-SENSITIVE replacements.
For more information about PowerShell's -replace operator see this answer.
Passing a script block ({ ... }) to dynamically determine the replacement string isn't supported in Windows PowerShell, so a Windows PowerShell solution requires direct use of the .NET [regex] class:
WinPs> [regex]::Replace('sometext.com' -replace '\.com$', '^s|t(?!$)', { param($m) $m.Value.ToUpper() })
SomeText
I have a powershell script, where I receive names of elements as a variables from Jenkins:
$IISarray = #("$ENV:Cashier_NAME", "$ENV:Terminal_NAME", "$ENV:Content_Manager_NAME", "$ENV:Kiosk_BO_NAME")
foreach ($string in $IISarray){
"some code goes here"
}
Sometimes random elements can be blank. How can I add a check to see if the current element in array is blank, skip it and go to next element?
It's easiest to use -ne '' to created a filtered copy of the array that excludes empty entries, courtesy of the ability of many PowerShell operators to act as a filter with an array-valued LHS.
Note: I'm assuming you mean to filter out empty strings, not also blank (all-whitespace) ones, given that undefined environment variables expand to an empty string.
# Sample array with empty elements.
# Note: No need for #(...), unless there's just *one* element.
$IISarray = "foo", "", "bar", "baz", ""
# Note the `-ne ''`, which filters out empty elements.
foreach ($string in $IISarray -ne ''){
$string # echo
}
The above yields:
foo
bar
baz
soundstripe's answer offers a Where-Object solution, which potentially provides added flexibility via the ability to specify an arbitrary filter script block, but the use of a pipeline is a bit heavy-handed for this use case.
Fortunately, PSv4+ offers the .Where() collection method, which performs noticeably better.
Let me demonstrate it with a solution that also rules out blank (all-whitespace) elements:
# Note the all-whitespace element, which we want to ignore too.
PS> ("foo", " ", "bar", "baz", "").Where({ $_.Trim() })
foo
bar
baz
Similar to the Where-Object cmdlet, you pass a script block to the .Where() method, inside of which the automatic $_ variable represents the input element at hand.
The .Trim() method trims leading and trailing whitespace from a string and returns the result.
An all-whitespace string therefore results in the empty string.
In a Boolean context (as the .Where() method script block implicitly is), the empty string evaluates to $false, whereas any non-empty string is $true.
You can choose to be explicit, however ($_.Trim() -ne ''), or even use a .NET method ([string]::IsNullOrWhiteSpace($_)).
You can use Where-Object to filter out null or empty values. It is very commonly used, so ? is shorthand for Where-Object.
$IISarray = #("$ENV:Cashier_NAME", "$ENV:Terminal_NAME", "$ENV:Content_Manager_NAME", "$ENV:Kiosk_BO_NAME")
foreach ($string in ($IISarray | ? {$_})){
"some code goes here"
}
The $_ is an automatic variable representing each incoming object in the pipeline. Both $null and the empty string '' are falsy in Powershell, so only non-null values with length > 0 will be passed in to your for loop.
# you can skip the `#` and brackets as well as the quotation marks
$IISarray = $ENV:Cashier_NAME, $ENV:Terminal_NAME, $ENV:Content_Manager_NAME, $ENV:Kiosk_BO_NAME
foreach($String in $IISarray) {
# trim the strings and check the length
if($String.Trim().Length -gt 0) {
"some code goes here"
}
}
I have a filename and I wish to extract two portions of this and add into variables so I can compare if they are the same.
$name = FILE_20161012_054146_Import_5785_1234.xml
So I want...
$a = 5785
$b = 1234
if ($a = $b) {
# do stuff
}
I have tried to extract the 36th up to the 39th character
Select-Object {$_.Name[35,36,37,38]}
but I get
{5, 7, 8, 5}
Have considered splitting but looks messy.
There are several ways to do this. One of the most straightforward, as PetSerAl suggested is with .Substring():
$_.name.Substring(35,4)
Another way is with square braces, as you tried to do, but it gives you an array of [char] objects, not a string. You can use -join and you can use a range to make that easier:
$_.name[35..38] -join ''
For what you're doing, matching a pattern, you could also use a regular expression with capturing groups:
if ($_.name -match '_(\d{4})_(\d{4})\.xml$') {
if ($Matches[1] -eq $Matches[2]) {
# ...
}
}
This way can be very powerful, but you need to learn more about regex if you're not familiar. In this case it's looking for an underscore _ followed by 4 digits (0-9), followed by an underscore, and four more digits, followed by .xml at the end of the string. The digits are wrapped in parentheses so they are captured separately to be referenced later (in $Matches).
Yet another approach: returns 1234 substring four times.
$FileName = "FILE_20161012_054146_Import_5785_1234.xml"
# $FileName
$FileName.Substring(33,4) # Substring method (zero-based)
-join $FileName[33..36] # indexing from beginning (zero-based)
-join $FileName[-8..-5] # reverse indexing:
# e.g. $FileName[-1] returns the last character
$FileArr = $FileName.Split("_.") # Split (depends only on filename "pattern template")
$FileArr[$FileArr.Count -2] # does not depend on lengths of tokens
I have a method that has an if statement that catches if it finds a special character. What I want to do now if find the position of the special characters and replace it with _A
Some Examples
test# becomes test_A
I#hope#someone#knows#the#answer# becomes I_Ahope_Asomeone_Aknows_Athe_Aanswer_A
or if it has more than one special character
You?didnt#understand{my?Question# becomes You_Adidnt_Aunderstand_Amy_AQuestion_A
Would I have to loop through the whole string and when I reach that character change it to _A or is there a quicker way of doing this?
# is just a character like any other, you can use the -replace operator:
PS C:\>'I#hope#someone#knows#the#answer#' -replace '#','_A'
I_Ahope_Asomeone_Aknows_Athe_Aanswer_A
Regex is magic, you can define all the special cases you like (braces will have to be escaped):
PS C:\>'You?didnt#understand{my?Question#' -replace '[#?\{]','_A'
You_Adidnt_Aunderstand_Amy_AQuestion_A
So your function could look something like this:
function Replace-SpecialChars {
param($InputString)
$SpecialChars = '[#?\{\[\(\)\]\}]'
$Replacement = '_A'
$InputString -replace $SpecialChars,$Replacement
}
Replace-SpecialChars -InputString 'You?didnt#write{a]very[good?Question#'
If you are unsure of which characters to escape, have the regex class do it for you!
function Replace-SpecialChars {
param(
[string]$InputString,
[string]$Replacement = "_A",
[string]$SpecialChars = "#?()[]{}"
)
$rePattern = ($SpecialChars.ToCharArray() |ForEach-Object { [regex]::Escape($_) }) -join "|"
$InputString -replace $rePattern,$Replacement
}
Alternatively, you can use the .NET string method Replace():
'You?didnt#understand{my?Question#'.Replace('#','_A').Replace('?','_A').Replace('{','_A')
But I feel the regex way is more concise