Beginner Problems with Equals Operator - powershell

everyone trying to learn Powershell off and on and I'm stuck on this problem. I cannot seem to find an equals operator that this code will accept at the = true portion .
Ive tried -eq, =, ==, and === .
Trying to get the Msg box to pop up if this Test-path command returns a true condition.
$wshell = New-Object -ComObject Wscript.Shell
If( Test-Path 'C:\wmw\~$test.xlsx' **= True)**
{
$wshell.Popup("Hey $Env:ComputerName This file is in use!",0,"test")}
else
{$wshell.Popup("Hey $Env:ComputerName This file is not in use!",0,"test")}

First of all, the literal for true is $true in PowerShell. And the operator for equality comparison is -eq. Then there is the issue that parameters to cmdlets start with - and you'd need to wrap the command in parentheses. Otherwise -eq would be interpreted as a (non-existent) parameter to Test-Path. So putting that all together:
If( (Test-Path 'C:\wmw\~$test.xlsx') -eq $True) { ... }
or, since if just needs a value that can be coerced to a boolean you don't even need the explicit comparison in most cases:
if (Test-Path 'C:\wmw\~$test.xlsx') { ... }
One hint for future exploration of the shell: Read the error messages. Most of the time they are helpful.
Omitting the parentheses and using -eq tells you about the fact that it's interpreted as a parameter:
Test-Path : A parameter cannot be found that matches parameter name 'eq'.
Same with = which is interpreted as a parameter value here:
Test-Path : A positional parameter cannot be found that accepts argument '='.
Using parentheses correctly and using -eq breaks the parser, admittedly:
You must provide a value expression following the '-eq' operator.
Unexpected token 'True' in expression or statement.
Missing closing ')' after expression in 'if' statement.
Unexpected token ')' in expression or statement.
Using parentheses and = is helpful again:
The assignment expression is not valid. The input to an assignment operator must be an object that is able to accept assignments, such as a variable or a property.

Related

Function parameter validation in Powershell

Why does a [string] casted parameter with the 'value' $null in this example never throw an error (empty or $null), but a string with the value '$null' always throws? I would expect if passing a mandatory parameter, it is checked for $null/emptyness and thus an error is always thrown in these cases:
Function test_M_NoE ( [Parameter(Mandatory=$true)] [ValidateNotNullOrEmpty()] [string] $x ) {}
# test cases. Uncomment one:
[string]$x = [string]$null
# $x = [string]$null
# [string]$x = $null
# $x = $null
"1:"; test_M_NoE [string]$x # never error
"2:"; test_M_NoE $x # always error
The reason this works:
test_M_NoE [string]$x
Is that [string]$x is not being interpreted the way you expect.
Let's change your test function definition to help us better see what's actually going on:
function test_M_NoE {
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$x
)
Write-Host "Argument value passed was: '$x'"
}
Now, let's try again:
PS ~> $x = $null
PS ~> test_M_NoE [string]$x
Argument value passed was: '[string]'
Aha! The argument expression [string]$x did not result in an empty string - it resulted in the literal string value [string].
This is due to the fact that PowerShell attempts to parse command arguments differently from anything else. From the about_Parsing help topic:
Argument mode is designed for parsing arguments and parameters for commands in a shell environment. All input is treated as an expandable string unless it uses one of the following syntaxes: [...]
So really, PowerShell interprets our argument expression like a double-quoted string:
test_M_NoE "[string]$x"
At which point the behavior makes sense - $x is $null, so it evaluates to an empty string, and the result of the expression "[string]$x" is therefore just [string].
Enclose the argument expression in the $(...) subexpression operator to have it evaluated as a value expression instead of as an expandable string:
test_M_NoE $([string]$x)

PowerShell if -like error

Not sure what's wrong with this script
$SSODelete= "Delete"
$SSOfiles = "SSo.xml"
$channlename = "INC8-Patch"
write-host $channlename
if ($channelname -like *INC8*){
$SSOarg = "$SSODelete $SSOfiles"
Write-Host $SSOarg
}
It throws following error:
You must provide a value expression following the '*' operator.
At line:5 char:25
+ if ($channelname -like *INC8*){
+ ~~~~~
Unexpected token 'INC8*' in expression or statement.
Tried using "*INC8*" and '*INC8' but it then it does not qualify if statement.
Adding screenshot for script and it's results
Simple Script and results
The PowerShell -like operator should be used with the matching string quoted, e.g., $variable -like "*pattern*". -like accepts the simple wildcard (*) only; you can compare against regular expressions (regexps) by using -match. See Get-Help about_Comparison_Operators.
Thanks , it was due to variable name i was comparing ... i need to be more cautious

Can I specify conditional default values for a parameter in PowerShell?

I thought if this was possible it might work using parameter sets so I tried the following:
Function New-TestMultipleDefaultValues {
[CmdletBinding(DefaultParameterSetName="Default1")]
param (
[Parameter(Mandatory,ParameterSetName="Default1")]$SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1,
[Parameter(ParameterSetName="Default1")]$Test1 = "Value1",
[Parameter(ParameterSetName="Default2")]$Test1 = "Value2"
)
$PSBoundParameters
}
Executing this to create the function results in the error Duplicate parameter $test1 in parameter list. so it doesn't look like this way is an option.
The only thing I can think of at this point is to do something like this:
Function New-TestMultipleDefaultValues {
param (
$SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1,
$Test1
)
if (-not $Test1 -and $SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1) {
$Test1 = "Value1"
} elseif (-not $Test1 -and -not $SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1) {
$Test1 = "Value2"
}
$Test1
}
Which works but seems ugly:
PS C:\Users\user> New-TestMultipleDefaultValues -SomeOtherThingThatIfSpecifiedShouldResultInTest1HavingValue1 "thing"
Value1
PS C:\Users\user> New-TestMultipleDefaultValues
Value2
PS C:\Users\user> New-TestMultipleDefaultValues -Test1 "test"
test
Any better way to accomplish this?
The following should work:
Since there is then no longer a need for explicit parameter sets, I've omitted them; without specific properties, the [Parameter()] attributes aren't strictly needed anymore either.
Function New-TestMultipleDefaultValues {
[CmdletBinding()]
param (
[Parameter()] $SomeOtherThing,
[Parameter()] $Test1 =
('Value2', 'Value1')[$PSBoundParameters.ContainsKey('SomeOtherThing')]
)
# * As expected, if -Test1 <value> is explicitly specified,
# parameter variable $Test1 receives that value.
# * If -Test1 is omitted, the expression assigns 'Value1` to $Test1
# if -SomeOtherThing was specified, and 'Value2' otherwise.
$Test1 # Output the effective value of $Test1
}
It is possible to use expressions as parameter default values.
The above code is an expression and therefore can be used as-is.
To use a single command (a call to a PowerShell cmdlet, function, script or to an external program) as an expression, enclose it in (...), the grouping operator.
In all other cases you need $(...), the subexpression operator (or #(...), the array-subexpression operator) to convert the code to an expression; these cases are:
A Throw statement (and, hypothetically, exit and return statements, but you wouldn't use them in this context)
A compound construct such as foreach, while, ...
Multiple commands, expressions, or compound constructs, separated with ;
However, it is safe to always use $(...) (or #(...)) to enclose the code that calculates the default value, which you may opt to do for simplicity.
These expressions are evaluated after the explicitly specified parameters have been bound, which allows an expression to examine what parameters have been bound, via the automatic $PSBoundParameters variable:
('Value2', 'Value1')[$PSBoundParameters.ContainsKey('SomeOtherThing')] is simply a more concise reformulation of
if ($PSBoundParameters.ContainsKey('SomeOtherThing')) { 'Value1' } else { 'Value2' }
that takes advantage of [bool] values mapping onto 0 ($false) and 1 ($true) when used as an array index (integer).
In PowerShell v7+ you could use a ternary conditional instead, which has the added advantage of short-circuiting the evaluation:
$PSBoundParameters.ContainsKey('SomeOtherThing') ? 'Value1' : 'Value2'
You may want to look at dynamic parameters. You declare a section called dynamicparams {} and inside you can create parameters on the fly.

How to provide Linux-style parameter names in a powershell script

I would like to create a Powershell script that takes parameters in the standard Linux style, i.e., --my-param, with a leading --. I thought this might be possible using the alias parameter attribute, as in
Param (
[parameter(Mandatory=$true)]
[alias("-my-param","p")]
[String]
$param
)
What I hoped with this was that the call
c:\src\ps\params.ps1 --my-param "x"
would be recognized as referring to the alias -my-param. Unfortunately, what I get is
C:\src\ps\params.ps1 : A positional parameter cannot be found that accepts argument 'x'.
At line:1 char:21
+ c:\src\ps\params.ps1 <<<< --my-param1 "x"
+ CategoryInfo : InvalidArgument: (:) [params.ps1], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,params.ps1
On the other hand, using the alias my-param in this lets me call the script with -my-param.
Is there a way of specifying parameter names with leading -- in Powershell?
Your syntax fails at tokenizer level. Compare:
[Management.Automation.PSParser]::Tokenize(
'Command -parameter',
[ref]$null
)
...and...
[Management.Automation.PSParser]::Tokenize(
'Command --parameter',
[ref]$null
)
As you can see former is seen by parser as parameter, latter - as argument.
So the only way would be parsing all arguments "internally" and guessing what is parameter (from your perspective), and what is argument.
I'm not aware of any libraries that will parse Unix-style parameters for you (which doesn't necessarily mean there isn't one...), but you could just not declare any parameters, and parse the parameters yourself in the body of the script.
This will create a hashtable of the parameters, where they keys are the parameter names and the values are the parameter values. Switch parameters will have null values.
$params = #{}
$MyInvocation.Line.Substring(($MyInvocation.Line.IndexOf('--') + 2)) -split ' --' | %{
$_ -match '(\S+) ?(.+)?' | Out-Null
$params.($matches[1]) = $matches[2]
}
$MyInvocation.Line gives you the command line that was used to invoke the script. $MyInvocation.Line.Substring(($MyInvocation.Line.IndexOf('--') + 2)) gives you everything following the first --.
$_ -match '(\S+) ?(.+)?' assigns the parameter name to the first match group, and the value to the second match group. The Out-Null prevents PowerShell from printing True for each iteration.
The reason I used (.+)? rather than (.*) is to make the values of switch parameters null. (.*) will match an empty string if there is nothing to match, making the value of $matches[2] an empty string, whereas (.+)? won't match, making $matches[2] null.
This is assuming that all parameters begin with --. If you want to allow a single hyphen, restrict single-dash parameter names to a single letter, or check for incorrectly declared parameters (for example throw an error if there's a triple-hyphen), you'll have to account for that in your code, but this is the basic idea.

Conditional logic no longer working as expected?

Its been a long day and I think I'm going mad. I wanted to test for a file and generate an email if none existed. Here it is pared down to its most minimal:
> IF('False' -eq (Test-Path D:\Scripts\SFTP\Import\*)){ECHO "SEND EMAIL"}
> SEND EMAIL
__________________________________________________________________________
> IF((Test-Path D:\Scripts\SFTP\Import\*) -eq 'False'){ECHO "SEND EMAIL"}
>
Why doesn't the second command work?
I've tried running the Test-Path outside of the 'IF' statement into a variable and then testing against that, again it doesn't work.
If I simply run the 'Test-Path' command I get a boolean 'False' as expected. I've used conditional logic in this way before and its worked.
What am I doing wrong?
The reason is this. In the first one you have a string as the first operand of the comparison. This forces PS to coerce the second operand to a string if possible. In this case that means calling the .ToString() method of the boolean which would return the 'False' string (if the boolean is actually false of course). In the second case though, you are presenting a boolean as the first operand, so the string is being coerced to a boolean. Obviously it is not working. To avoid these issues, use the builtin $false (or $true) variable. Personally I would suggest just negating the Test-Path. Here are a couple of ways that should work:
if( -NOT (Test-Path D:\Scripts\SFTP\Import\*)){
if( (Test-Path D:\Scripts\SFTP\Import\*) -eq $false){
For the coercing rules of powershell
'False' -eq (Test-Path D:\Scripts\SFTP\Import\*)
the second value of comparision is evaluated as [string]
here
(Test-Path D:\Scripts\SFTP\Import\*) -eq 'False'
the second value of comparison can't be evaluated as [Bool] then it fails.
For bool comparin is optima use the automatic variable $false and $true