PS 4.0 Parameters with Space - Catch Error - powershell

I'm executing script in batch and passing 5 parameters separated by a comma via Java. PS 4.0 accepts parameters without the "".
[CmdletBinding()]
Param (
[Parameter(Position = 0)]
[string[]] $inpParms = $(throw 'Failure : This is required.')
)
I invoke the script like
myScript.ps1 user,pwd,Server Name, DLName,Action
It errors out due to the space in "Server Name". Throws error which spits back the parameters:
A positional parameter cannot be found that accepts argument 'System.Object[]'.
At line:1 char:1
+ .\myScript.ps1 user,pwd,Server Name, DLName,Create
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [ExchangeDL.ps1], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,ExchangeDL.ps1
Is there a way to trap this error so I don't spit sensitive info in logs?

While you don't have to quote arguments that don't have shell metacharacters in them, a space is a metacharacter, so you have two choices:
`-quote the metacharacters individually:
./myScript.ps1 user, pwd, Server` Name, DLName, Create
Or use "..." (interpolating) or '...' (literal) to enclose entire arguments as needed:
./myScript.ps1 user, pwd, 'Server Name', DLName, Create
Generally, though, your script will be more maintainable if you define individual parameters rather than a single array.
If you want to catch an incorrect invocation attempt, use a Try / Catch statement:
Try {
./myScript.ps1 user, pwd, Server Name, DLName, Action
} Catch {
Throw "Invocation of ./myScript.ps1 failed."
}
By not using the information from the statement-terminating error that the incorrect invocation produced (accessible as $_ in the Catch block) in the Throw statement, the original command line is not leaked.

Related

String comparison not working in powershell

I am trying an if else condition in powershell using string comparison. I tried as per documentation using -eq operator. But getting below error. Here "Build.Reason" is a predefined variable. Not sure why its looking for cmdlet name for variable.
Write-Host "$(Build.Reason)"
if ($(Build.Reason) -eq "Manual" ) {
$temp = "https://url/api/qualitygates/project_status?&pullRequest=$(Build.Reason)"
Write-Host "Manual"
} else {
Write-Host "CI"
}
Error
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -NoLogo -NoProfile -NonInteractive -ExecutionPolicy Unrestricted -Command ". 'D:\a\_temp\d7af16d6-ce3e-4dec-a636-9447962fdac4.ps1'"
Manual
Manual : The term 'Manual' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At D:\a\_temp\d7af16d6-ce3e-4dec-a636-9447962fdac4.ps1:7 char:5
+ if (Manual -eq "Manual" ) {
+ ~~~~~~
+ CategoryInfo : ObjectNotFound: (Manual:String) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CommandNotFoundException
It looks like $(Build.Reason) is a macro-style value provide by a CI system (it is not a PowerShell construct), which is expanded to become a literal part of the code before PowerShell sees it.
Therefore, if this value is to be treated as a string in the resulting PowerShell code, you need to quote it; e.g.:
if ("$(Build.Reason)" -eq "Manual") { # ...
Note that if there's a chance that $(Build.Reason) expands to a value with embedded " characters, they would have to be escaped as `". Similarly, if the value contains embedded $ chars., single-quoting should be used, which may then require escaping embedded single quotes as ''.
If this escaping cannot be performed at the source, you can use a verbatim here-string:
if (#'
$(Build.Reason)
'# -eq 'Manual') { # ...
Important: The closing '# must always be at the very beginning of the line.

Passing integer array as command line argument to powershell script

I have the following powershell script:
param (
[Parameter(Mandatory=$true)][int[]]$Ports
)
Write-Host $Ports.count
foreach($port in $Ports) {
Write-Host `n$port
}
When I run the script with $ powershell -File ./test1.ps1 -Ports 1,2,3,4 it works (but not as expected):
1
1234
When I try to use larger numbers, $ powershell -File .\test.ps1 -Ports 1,2,3,4,5,6,10,11,12, the script breaks entirely:
test.ps1 : Cannot process argument transformation on parameter 'Ports'. Cannot convert value "1,2,3,4,5,6,10,11,12" to type "System.Int32[]". Error: "Cannot convert value "1,2,3,4,5,6,10,11,12" to type "System.Int32". Error: "Input
string was not in a correct format.""
+ CategoryInfo : InvalidData: (:) [test.ps1], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,test.ps1
It seems like powershell is trying to process any numbers passed via the Ports param as a single number, though I'm not sure why this is happening, or how to get past it.
The issue is that a parameter passed through powershell.exe -File is a [string].
So for your first example,
powershell -File ./test1.ps1 -Ports 1,2,3,4
$Ports is passed as [string]'1,2,3,4' which then attempts to get cast to [int[]]. You can see what happens with:
[int[]]'1,2,3,4'
1234
Knowing that it will be an just a regular [int32] with the comma's removed means that casting 1,2,3,4,5,6,10,11,12 will be too large for [int32] which causes your error.
[int[]]'123456101112'
Cannot convert value "123456101112" to type "System.Int32[]". Error: "Cannot convert value "123456101112" to type "System.Int32". Error: "Value was either too
large or too small for an Int32.""
To continue using -file you could parse the string yourself by splitting on commas.
param (
[Parameter(Mandatory=$true)]
$Ports
)
$PortIntArray = [int[]]($Ports -split ',')
$PortIntArray.count
foreach ($port in $PortIntArray ) {
Write-Host `n$port
}
But luckily that is unnecessary because there is also powershell.exe -command. You can call the script and use the PowerShell engine to parse the arguments. This would correctly see the Port parameter as an array.
powershell -Command "& .\test.ps1 -Ports 1,2,3,4,5,6,10,11,12"

Why passing args from powershell gives error while working from inside script itself

Why calling .\MyScript.ps1 -Uninstall from Powershell gives an error
+ Super-Function $Args
+ ~~~~~
+ CategoryInfo : InvalidData : (:) [Super-Function], ParameterBindingArgumentTransformationException
+ FullyQualifiedErrorId : ParameterArgumentTransformationError,Super-Function
While calling "Super-Function" from the script itself with Super-Function -Uninstall , replacing $Args with the switch works ?
Why copy pasting the function on Powershell and then going for Super-Function -Uninstall works too ?
Here's the content of MyScript.ps1
function Super-Function([parameter(Mandatory=$False)][ValidateScript({Test-Path _$})][String]$var1 = ".\MyFile.ext",
[parameter(Mandatory=$False)][ValidateScript({Test-Path _$})][String]$var2 = "HKLM:\SOFTWARE\Wow6432Node\Publisher\SoftwareName",
[parameter(Mandatory=$False)][ValidateScript({([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")})][Switch]$Uninstall,
[parameter(Mandatory=$False)][ValidateScript({([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")})][Switch]$Install
)
{
}
Super-Function $Args
You have a few issues there that I see. Your ValidateScript for each argument have an issue. First, might just be a typo, you have the characters backwards for current pipe item. Should be $_ instead of _$. Next I find it befuddling that you test the presence of the admin role against a couple of boolean switches. Lets just move that inside the function (If what you had works that is fine. Just does make much sense)
Lastly, and most importantly, what you are trying to do with $args is called splatting. Use #args which will splat the hashtable of arguments, passed in from the script, against the function.
function Super-Function{
param(
[parameter(Mandatory=$False)][ValidateScript({Test-Path $_})][String]$var1 = ".\MyFile.ext",
[parameter(Mandatory=$False)][ValidateScript({Test-Path $_})][String]$var2 = "HKLM:\SOFTWARE\Wow6432Node\Publisher\SoftwareName",
[parameter(Mandatory=$False)][Switch]$Uninstall,
[parameter(Mandatory=$False)][Switch]$Install
)
# Use this to verify what has been assinged to your parameters. Will not show default values.
#$PSBoundParameters
If(([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")){
"Sure"
} Else {
Throw "Nope"
}
}
Super-Function #args

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.

Powershell: Args/params not being populated

I have a PowerShell script:
param (
[Parameter(Mandatory=$true)][string]$input,
[Parameter(Mandatory=$true)][string]$table
)
Write-Host "Args:" $Args.Length
Get-Content $input |
% { [Regex]::Replace($_, ",(?!NULL)([^,]*[^\d,]+[^,]*)", ",'`$1'") } |
% { [Regex]::Replace($_, ".+", "INSERT INTO $table VALUES (`$1)") }
The Write-Host part is for debugging.
I run it as .\csvtosql.ps1 mycsv.csv dbo.MyTable (from powershell shell), and get
Args: 0
Get-Content : Cannot bind argument to parameter 'Path' because it is an empty s
tring.
At C:\temp\csvtosql.ps1:7 char:12
+ Get-Content <<<< $input |
+ CategoryInfo : InvalidData: (:) [Get-Content], ParameterBinding
ValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorEmptyStringNotAl
lowed,Microsoft.PowerShell.Commands.GetContentCommand
I get exactly the same error with any parameters that I pass, also the same error if I try to use named parameters.
What can cause parameters not to be passed in?
UPDATE: PowerShell ISE asks me for these parameters using GUI prompts, then gives me the same error about them not being passed in.
Unless you marked a parameter with the ValueFromRemainingArguments attribute (indicates whether the cmdlet parameter accepts all the remaining command-line arguments that are associated with this parameter), Args is "disabled". If all you need is the arguments count call the special variable:
$PSBoundParameters.Count
Do not mix. Make use of $args or parameters.
Also do note that $input is a special variable, don't declare it as a parameter. http://dmitrysotnikov.wordpress.com/2008/11/26/input-gotchas/
You're calling your script with positional parameters (i.e. unnamed) and PowerShell doesn't know how to map them to your script parameters. You need to either call your script using the parameter names:
.\csvtosql.ps1 -input mycsv.csv -table dbo.MyTable
or update your script to specify your preferred order of positional parameters:
param (
[Parameter(Mandatory=$true,Position=0)]
[string]
$input,
[Parameter(Mandatory=$true,Position=1)]
[string]
$table
)