Small problem with (if) command in powershell - powershell

First, let me show you the code
$url = Read-Host 'URL'
if ( $url -gt '.'){
msg * "The title does not contain(.)"
}
The code should alert you that the site does not have a dot
But either way the code alerts me
The code does not give me any error

The -gt operator does a case-insensitive lexical comparison, and where non-letters are involved, it uses case-folded Unicode ordering. The . is ordered before any of the letters, so virtually any URL, with or without a dot, will be lexically greater than the single dot you are comparing against.
If you want to test whether a string contains a dot, you should most likely be using the -match or -notmatch operators; note that this uses regular expressions.
$url = Read-Host 'URL'
if ( $url -notmatch '\.'){
msg * "The title does not contain(.)"
}

Related

Extract a string from string, choosing the method

I have a string which looks like this:
[Error] Failed to process site: https://xxxxxxxxx/teams/xxxxxx. The remote server returned an error: (404) Not Found.
The challenge is to extract highlighted part of this string.
Tried some split operation but without any success.
Use the -match operator to perform a regex search for the URL, then extract the matched string value:
# define input string
$errorString = '[Error] Failed to process site: https://some.domain.tld/teams/xxxxxx. The remote server returned an error: (404) Not Found.'
# define regex pattern for a URL followed by a literal dot
$urlFollowedByDotPattern = 'https?://(?:www\.)?[-a-zA-Z0-9#:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()#:%_\+.~#?&//=]*)(?=\.)'
# perform match comparison
if($errorString -match $urlFollowedByDotPattern){
# extract the substring containing the URL matched by the pattern
$URL = $Matches[0]
# remove everything before the last slash
$pathSuffix = $URL -replace '^.*/'
}
If the URL was found, the $pathSuffix variable now contains the trailing xxxxxx part
To offer a concise alternative to Mathias R. Jessen's helpful answer using the regex-based -replace operator:
-replace can be special-cased for substring extraction by:
formulating a regex that matches the entire input string
using a capture group ((...)) inside that regex to capture the substring of interest and using that as the replacement string; e.g., $1 in the replacement string refers to what the first capture group captured.
$str = '[Error] Failed to process site: https://xxxxxxxxx/teams/xxxxxx. The remote server returned an error: (404) Not Found.'
$str -replace '.+https://.+/([^/]+)\..+', '$1' # -> 'xxxxxx'
For an explanation of the regex and the option to experiment with it, see this regex101.com page.
Note:
If the regex does not match, the whole input string is returned as-is.
If you'd rather return an empty string in that case, use a technique suggested by zett42: append |.* to the regex, which alternatively (|), if the original regex didn't match, unconditionally matches the whole input string with .*, in which case - since no capture group is then used - $1 evaluates to the empty string as the effective return value:
# Note the addition of |.*
'this has no URL' -replace '.+https://.+/([^/]+)\..+|.*', '$1' # -> ''
regex101.com page.
If such a regex is too mind-bending, you can try a multi-step approach that combines -split and -like:
$str = '[Error] Failed to process site: https://xxxxxxxxx/teams/xxxxxx. The remote server returned an error: (404) Not Found.'
# -> 'xxxxxx'
# Note the line continuations (` at the very end of the lines)
# required to spread the command across multiple lines for readability.
(
-split $str <# split into tokens by whitespace #> `
-like 'https://*' <# select the token that starts with 'https://' #> `
-split '/' <# split it into URL components by '/' #>
)[-1]?.TrimEnd('.') <# select the last component and trim the traiing "." #>
Note:
The use ?., the null-conditional member-access operator, prevents a statement-terminating error if no token of interest is found ($null is returned instead), but it requires PowerShell (Core) 7.1+.
In earlier versions or alternatively, you can replace ?.TrimEnd('.') with -replace '\.$', which defaults to an empty string instead.

Verifying a string is not empty/null in an If condition

I'm writing a script that will accept user input via Read-Host (set to $String), and I want to avoid any issues that could be caused by having a blank value for the variables. Since I'll be using this a lot, I want to implement it into a function that verifies no invalid characters are being used.
I thought I could use an if statement with ![string]::IsNullOrEmpty($String) as one of the conditions:
Function Test-ValidCharacters ($String, $ValidCharacters) {
if (($String -match $ValidCharacters) -and (!([string]::IsNullOrEmpty($String)))) {
return $true
}
else {return $false}
}
I also tried this:
Function Test-ValidCharacters ($String, $ValidCharacters) {
if (($String -match $ValidCharacters) -and ($String -ceq "")) {
return $true
}
else {return $false}
}
In both of these cases, I can just hit enter when presented with the $String's Read-Host prompt and the script will behave as if the function returned $True (and then later encounter fatal errors). The other half works - if I include characters not specified by $ValidCharacters the function returns $False as expected.
I am sure I'm missing something here. I even tried doing a second nested if statement and got the same result.
Edit: Here's the code snippet where I call the function and notice the issue.
$ValidCharacters = '[^a-zA-Z0-9]'
$FirstN = Read-Host -Prompt "New user's first name"
While (Test-ValidCharacters $FirstN $ValidCharacters -eq $false) {
Write-Output "$FirstN contains illegal characters. A-Z, a-z, and 0-9 are accepted."
$FirstN = Read-Host -Prompt "New user's first name"
}
Assuming $ValidCharacters isn't itself an empty string and contains an anchored character-range regex (regular expression) that covers the entire input string, such as ^[a-z0-9./:]+$, given that the -match operator matches any substring by default (note that a better name for the parameter is therefore something like $ValidationRegex):[1]
In the first function definition, the RHS of your -and operation is redundant - it adds nothing to the conditional, because if $String -match $ValidCharacters is $true, then so is ! [string]::IsNullOrEmpty($String), by definition.
Conversely, in the second function definition your -and operation always returns $false, because $String -ceq "" is by definition $false, if the LHS returned $true.
Assuming that your intent is to prevent empty or all-whitespace input and to ensure that any string - trimmed of incidental leading and/or trailing whitespace - is composed only of expected characters, use the following:
Function Test-ValidCharacters ($String, $ValidCharacters) {
# Note: no strict need for `return`.
$String.Trim() -match $ValidCharacters
}
[1] Alternatively, stick with $ValidCharacters and pass a regex that describes only a single valid character, such as '[a-z0-9./:]', and construct the entire-string matching regex inside the function with '^' + $ValidCharacters + '+$'

Powershell: specify email:password with random data in between

Similar like this - Extract email:password
However we have here the situation that in some files there is other data between the data I want to parse, as example:
email:lastname:firstname:password or email:lastname:firstname:dob:password
So my question is - with which command would I be able to ignore 2 segments like "lastname:firstname" or even 3 parts "lastname:firstname:dob". I am using the below regex to retrieve email:password from a big list.
$sw = [System.IO.StreamWriter]::new("$PWD/out.txt")
switch -regex -file in.txt {
'(?<=:)[^#:]+#[^:]+:.*' { $sw.WriteLine($Matches[0]) }
}
$sw.Close()
You need to refine your regex:
# Create sample input file
#'
...:foo#example.org:password1
...:bar#example.org:lastname:firstname:password2
...:baz#example.org:lastname:firstname:dob:password3
'# > in.txt
# Process the file line by line.
switch -regex -file in.txt {
'(?<=:)([^#:]+#[^:]+)(?:.*):(.*)' { '{0}:{1}' -f $Matches[1], $Matches[2] }
}
For brevity, saving the output to a file was omitted above, so the email-password pairs extracted print to the screen by default, namely as:
foo#example.org:password1
bar#example.org:password2
baz#example.org:password3
Explanation of the regex:
(?<=:) is a positive lookbehind assertion for ensuring that matching starts right after a : character.
Note: I based this requirement on your original question and its sample data.
([^#:]+#[^:]+) uses a capture group (capturing subexpression, (...)) to match an email address up to but not including the next :.
(?:.*): uses a non-capturing subexpression ((?:...)) that matches zero or more characters (.*) unconditionally followed by a :
(.*) uses a capture group to capture all remaining characters after what is effectively the last : on each line, assumed to be the password.
$Matches[1] and $Matches[2] refer to the 1st and 2nd capture-group matches, i.e. the email address and the password.
Assuming you had data like this:
"lastname:firstname"
"lastname:firstname:dob"
"lastname:firstname:password:somepassword"
"lastname:john:firstname:jacob:password:dingleheimershmit
You can move through each row like this:
$items = gc .\stack.txt
ForEach($item in $items){
}
Then we can split each row on a : character and check each of those to see if its a match for the string passwrod. If it is, then we check the next token in the row which should be a password.
This code will get you going, you'll just need to do something meaningful with $password.
$items = gc .\stack.txt
ForEach($item in $items){
"processing $item"
$tokens = $item.Split(":")
For($x=0; $x -lt $tokens.Count;$x++){
$token = $tokens[$x]
#"Checking if $token is like password"
if ($token -match "password"){
"since this token is like password, checking next token which should be a password"
$password = $tokens[$x+1]
Write-Host -ForegroundColor Yellow $password
}
}
}

Ignore Tokens on the Front of a Regular Expression in Powershell

Hi so I have read over regular expressions and all that but don't really fully understand it. Just looking for a little help here after a lot of searching on here and google.
I have an XML file that I am editing but for now let's pretend I'm doing just a single string. This works great except that I lose Connection Database="SQLEventLog" text in the replace. What kind of ignore token do I use here?
Here is my code
$passedString = '<Connection Database="SQLEventLog" >Data
Source=;Initial Catalog=Connector;Integrated Security=True</Connection>'
search($passedString)
function search ($string)
{
$pattern = '*Data Source=*'
if ($string -like '*Data Source=*')
{
Write-Warning 'found'
$string = $string -replace '.*Data Source=*', 'Data
Source=localhost'
}
Write-Warning $string
}
So, a few things. Best-practice for defining function parameters is to use the Param() clause. Functions in PowerShell are not called with parenthesis, but are separated by spaces (e.g. Function arg1 arg2 Arrayarg3,Arrayarg3)
Additionally, the -like comparison operator does not use regex, it's a wildcard comparison. I've updated your example to accomplish your goal.
Function Search
{
Param($String)
If ($String -like '*Data Source=*')
{
Write-Warning 'found'
$string = $string -replace 'Data\sSource=', 'Data Source=localhost'
}
Write-Warning $string
}
$passedString = 'Data Source=;Initial Catalog=Connector;Integrated Security=True'
Search $passedString
Note: the -replace function DOES use regex for the first piece.

What is the difference between $a = $(Read-Host) and $a = (Read-Host)?

I have to review some scripts what my Ex-Colleague left behind and I am very curious why he is using $a = $(Read-Host -Prompt "Write something") in the Parameters section rather than $a = (Read-Host -Prompt "Write something").
I have tested it in a single script but haven't seen any difference so far.
I have searched thru the google and StackOverFlow but haven't find any clue so far what could be the difference.
Maybe my searching methods are bad.
Quoting from Keith Hill's blog:
What’s the difference between grouping expressions (), subexpressions $() and array subexpressions #()? A grouping expression can contain just a single statement. A subexpression can contain multiple semicolon separated statements. The output of each statement contributes to the output of the subexpression. An array subexpression behaves just like a subexpression except that it guarantees that the output will be an array. The two cases where this makes a difference are 1) there is no output at all so the result will be an empy array and 2) the result is a scalar value so the result will be a single element array containg the scalar value. If the output is already an array then the use of an array subexpession will have no affect on the output i.e. array subexpressions do not wrap arrays inside of another array.
Silly example:
$a = (Read-Host -Prompt 'something'; echo 'foo')
$b = $(Read-Host -Prompt 'something'; echo 'foo')
The first statement (with the grouping expression) will throw an error, because you cannot have multiple statements in a grouping expression. The second statement will work and append a line "foo" to the text entered via Read-Host.
In your example scenario ($a = (Read-Host -Prompt 'something') vs. $a = $(Read-Host -Prompt 'something')) it doesn't make any difference. More precisely, you shouldn't use either grouping expression or subexpression operator in that scenario, because they have no purpose there.
$() is for evaluating subexpressions in strings. It does nothing here.
You don't need () around read-host either, you can simply use $a = Read-Host -Prompt 'your input here'
(-Prompt needs a string as parameter).