I'd like to pass a custom made object from script to another.
Beginning of subscript.ps1 there are input parameters:
param(
[string]$someString,
[object]$custClassData
)
In the main.ps1 I'm trying to call the subscript.ps1 after introducing a custom object:
class custClass{
[string]$string1
[string]$string2
[string]$string3
}
$customizedObject = [custClass]::new()
$customizedObject.string1 = "smthng1"
$customizedObject.string2 = "smthng2"
$customizedObject.string3 = "smthng3"
$scriptPath = ".\subscript.ps1"
$smString = "somethingsomething"
powershell.exe -file $scriptPath -someString $smString -custClassData $customizedObject
When calling like this if i check in subscript $custClassData.GetType it returns System.String, so i only get the name of the object there. If I generate class and object in the powershell manually and put data there and pass it to the subscript the type is custClass.
In subscript.ps1 the $custClassData parameter needs to validate the type [CustClass] not [object]. So something like:
param(
[string]$someString,
[CustClass]$custClassData
)
This way the data that is passed to that parameter must be of the type [CustClass].
Additionally, the way you are calling subscript.ps1 does not look correct. You do not need to call powershell.exe in order to invoke subscript.ps1. powershell.exe will always throw an error here.
You should change subscript.ps1 to subscript.psm1, and turn the contents of the script into a function, and use it like this:
In subscript.psm1:
function Do-TheNeedful {
param(
[string]$someString,
[CustClass]$custClassData
)
#~
# do work
#~
}
In main.ps1
class custClass{
[string]$string1
[string]$string2
[string]$string3
}
Import-Module subscript.psm1
$customizedObject = [custClass]::new()
$customizedObject.string1 = "smthng1"
$customizedObject.string2 = "smthng2"
$customizedObject.string3 = "smthng3"
Do-TheNeedful -someString "a_string" -custClassData $customizedObject
Calling powershell.exe casts everything to strings. Launch the script file directly instead:
File: sub.ps1
param(
[object]$foo
)
$foo
File: main.ps1
class myClass{
[string]$A
}
$myObject = [myClass]::new()
$myObject.A = "BAR"
.\sub.ps1 $myObject
Related
i m writing a script that makes use of a cmdlet from a console app, let's call the cmdlet as cmdA. I have no control over implementation of cmdA. cmdA takes parameters with a double dash (cmdA --param1 value1 --param2 value2 --param3 value3 --param4 value4 --param5 value5)
Now param2, param3, param4 and param5 are optional. The user of my script may or may not provide values for these optional parameters. In the script, i formed a hashtable using the parameters for which the user provided values. Let's call it paramHashtable.
I am executing cmdA #paramHashtable but this is not working. I think it has to do something with double dashes because this approach works fine with a cmd that takes parameters with single dash
What would be an efficient way to pass parameters to the cmdA (an inefficient way would be to use many if blocks to check which values were provided and make calls accordingly)
edited*
This is how i m creating the hashtable :-
$optionalParameters = #{}
$optionalParameters = ParameterSelection ${OutputFolder} ${PerfQueryIntervalInSec} ${StaticQueryIntervalInSec} ${NumberOfIterations}
$requiredParameters = #{"sqlConnectionStrings " = "$SqlConnectionStrings"}
$parameters = $requiredParameters + $optionalParameters
function ParameterSelection ([string] $outputFolder, [string] $perfQueryIntervalInSec, [string] $staticQueryIntervalInSec, [string] $numberOfIterations)
{
$parametersList = #{}
if($outputFolder -ne "")
{
$parametersList["outputFolder "] = $outputFolder
}
if($perfQueryIntervalInSec -ne "")
{
$parametersList["perfQueryIntervalInSec "] = $perfQueryIntervalInSec
}
if($staticQueryIntervalInSec -ne "")
{
$parametersList["staticQueryIntervalInSec "] = $staticQueryIntervalInSec
}
if($numberOfIterations -ne "")
{
$parametersList["numberOfIterations "] = $numberOfIterations
}
return $parametersList
}
This is how i m calling it :-
& $ExePath actionName #parameters
The $ExePath has the path of the program to be executed
The actionName takes parameters like this:-
actionName --sqlConnectionStrings "Data Source=Server1" --outputFolder C:\Output
Can splatting with hash table work on cmdlets / functions where it's parameter have dashes?
It may work, but it is definitely not a good idea to have parameter names with dashes as this will result in a function / cmdlet where named parameters cannot be used, PowerShell binds the arguments positionally! (thanks mklement0 for pointing this out):
function Test-Splatting {
param(${-param1}, ${-param2})
"${-param1} ${-param2}"
}
$param = #{ '--param1' = 'hello'; '--param2' = 'world' }
Test-Splatting #param # => hello world
Example of what was mentioned before using the same function above:
# --param1 is bound positionally and not interpreted as a parameter:
Test-Splatting --param1 hello # => --param1 hello
As for an external programs, the linked answer in comments explains very well the approach you could take using a wrapper function and the use of the automatic variable $args:
function myfunc {
$binaryPath = 'path/to/file.exe'
& $binaryPath actionName $args
# or #args we can't be sure until testing
}
myfunc --sqlConnectionStrings "Data Source=Server1" --outputFolder C:\Output
As noted in the comments, because you are calling an external program, you should use array-based splatting or simply arrays directly to pass programmatically constructed arguments.
By contrast, hashtable-based splatting is usually only helpful when calling PowerShell commands (while it technically works with external programs too, the resulting parameter format (e.g. -foo:bar or -foo:"bar none") is unusual and understood by few external programs)
Note that the parameter names and values must be passed as separate array elements and the elements representing parameter names must include the - or -- prefix; e.g., consecutive array elements
'--sqlConnectionStrings' (the name) and
'Data Source=Server1' (the value).
PowerShell then constructs a command line for invocation of the external program behind the scenes, space-separating the array elements and double-quoting elements with embedded spaces on demand; the above example turns into the following:
--sqlConnectionStrings "Data Source=Server1"
Note that it is up to the target program to parse the single string that is its command line in a way that recognizes parameter name-value pairs.
# Get the array of *optional* parameter names and values from your helper function.
$optionalParameters = ParameterSelection ${OutputFolder} ${PerfQueryIntervalInSec} ${StaticQueryIntervalInSec} ${NumberOfIterations}
# Declare the *required* parameter(s) as an array.
$requiredParameters = '--sqlConnectionStrings', $SqlConnectionStrings
# Pass the arrays as-is to the external program.
# (Empty arrays are effectively ignored.)
& $ExePath actionName $requiredParameters $optionalParmeters
Your ParameterSelection function can be simplified as follows:
function ParameterSelection ([string] $outputFolder, [string] $perfQueryIntervalInSec, [string] $staticQueryIntervalInSec, [string] $numberOfIterations)
{
# Loop over all bound parameters.
$PSBoundParameters.GetEnumerator() | ForEach-Object {
# Emit the parameter name prefixed with '--' and the value.
# PowerShell automatically collects the output strings in an array
# when you assign a call to this function to a variable.
'--' + $_.Key
$_.Value
}
}
I need help understanding how to pass on an argument from an imported module.
The module contains some custom arguments such as -one, -two, -three
I am trying to make a GUI using the commands from the module.
eg. If "One" is selected from the drop down menu, pass through the -one command.
However when I do so (using the example below), I get the error: "A positional parameter cannot be found that accepts argument '-one'."
I can see that using the code below, it adds single quotations around the command which probably breaks it.
I know I can run an IF statement (eg if combobox.text = "one", do this), however I would prefer to use a variable instead of having to make multiple if statements or a loop. The use of a variable seems like a simpler option.
I'm learning this language as I go so I'm not quite there yet with the knowledge :)
Thanks for any help. Hope this made sense.
$variable = $comboboxNumbers.Text
#example One is selected from the dropdown
Custom-ADCommand -identity "username" $variable
Below is simple example method:
function Set-SwitchParams {
[CmdletBinding()]
param (
# Parameter help description
[Parameter(Mandatory = $false)]
[switch]
$SwitchA,
[Parameter(Mandatory = $false)]
[switch]
$SwitchB
)
begin {
}
process {
}
end {
if ($SwitchA){
Write-Host "SwitchA is activated"
}
if ($SwitchB){
Write-Host "SwitchB is activated"
}
}
}
Put the method in a PS1 file, e.g. SwitchPlayground.ps1. Then source the file in PowerShell via:
. .\SwitchPlayground.ps1
Afterward, you can play around with the command, e.g.:
Set-SwitchParmas -SwitchA
I'd suggest studying the following links:
about functions basic
about functions advanced
about function parameters
Hope that helps.
An If statement if probably much nicer, but its possible to create a string and then execute the string in powershell.
As a simple example take this string
$string = '#("test","hello","whats up")'
I can then execute it and use it to create an array
$array = invoke-expression $string
Which will create an array with "test", "hello" and "whats up" and store it in $array
PS C:\temp> $string = '#("test","hi","what")'
PS C:\temp> $array = Invoke-Expression $string
PS C:\temp> $array
test
hi
what
I have the following function.
function Params {
param (
[Parameter(Mandatory = $true)]
[Alias('Param1')]
[AllowNull()]
${Param1[default value]}
)
[ref] $Param1 =
if (${Param1[default value]}) {
${Param1[default value]}
} else {
'default'
}
}
Params
$input1 = $null
"the param input is $([ref]$input1)"
If i input something for parameter on prompt or if i leave it to default value, i get this as output for $([ref]$input)
the param input is System.Management.Automation.PSReference`1[System.Management.Automation.LanguagePrimitives+Null]
Why am i not getting a value instead?
I want this output for example:
the param input is default
I ended up resorting to a different method to achieve what i want:
Defining this at the top of script:
[CmdletBinding()]
Param(
$Param1 = (Read-Host -prompt "Param1")
)
if (!$Param1) { "default" }
"the param input is $Param1"
The [ref] type accelerator (it's not a type accelerator in the usual sense, but it does create PSReference objects, so... it sort of is) gets you, as it tells you, a PSReference object.
In order to retrieve the value from it, you'd need to ask for it specifically. In your code, you can access it by pulling the Value property from the created reference object.
"the param input is $(([ref]$input1).Value)"
However, given that $input1 isn't assigned to, you might have to refactor a bit to get what you're after.
I'm trying to import an xml file and store as a variable for the remainder of a powershell session. The import is obviously successful but the variable content does not persist outside of the function.
Function auth
{
$cred = import-clixml -Path c:\temp\cred.xml
}
try this:
Function auth
{
$global:cred = "test"
}
auth
$global:cred
You can use globals as Esperento57 suggests or you can do this
function auth
{
return 'test'
}
$cred = auth
More succinct:
function auth
{
'test'
}
$cred = auth
You need to declare the variable outside the scope of the function first and then inside the function explicitly tell the variable to update using the script:var method.
Here's the example is taken from https://www.kongsli.net/2013/04/25/powershell-gotchas-refer-to-variables-outside-a-function/ to which credit is given.
The thing is that we have to explicitly tell Powershell to update the variable in the parent scope instead of creating a new variable in the current scope.
$x = 1
function changeit {
"Changing `$x. Was: $x"
$script:x = 2
"New value: $x"
}
"`$x has value $x"
changeit
"`$x has value $x"
If you need to do this but with a number of functions and variables, you can place them all into a script and then dotsource the script.
Imagine a script like this:
#MyDevFunctions.ps1
$myImportantVar = "somevar"
$myOtherVar = "ABC123"
Function Get-MyCoolValue(){$myImportantVar}
Write-Host "Finished Loading MyDevFunctions"
If you wanted to run this, and then also persist the values of the variables and also the functions themselves, from your parent script you simply invoke it like so:
PS > . .\MyDevFunctions.ps1
"Finished Loading MyDevFunctions"
PS > $myOtherVar
ABC123
PS> Get-MyCoolValue
someVar
I have two functions. The first has one parameter set, the second has two parameter sets as follows:
function OneSet{
[CmdletBinding()]
param ( $NoSet,
[parameter(ParameterSetName = 'A')]$A )
process { $PSCmdlet.ParameterSetName }
}
function TwoSets{
[CmdletBinding()]
param ( $NoSet,
[parameter(ParameterSetName = 'A',Mandatory = $true)]$A,
[parameter(ParameterSetName = 'B',Mandatory = $true)]$B )
process { $PSCmdlet.ParameterSetName }
}
Invoking the first one without arguments results in '__AllParameterSets' binding:
C:\> OneSet
__AllParameterSets
Invoking the second one without arguments throws an exception:
C:\> TwoSets
TwoSets : Parameter set cannot be resolved using the specified named parameters.
+ TwoSets
+ ~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [TwoSets], ParameterBindingException
+ FullyQualifiedErrorId : AmbiguousParameterSet,TwoSets
I don't see why the second case is any more ambiguous than the first. Why doesn't PowerShell bind to TwoSets using the "__AllParameterSets" parameter set?
Is there a (terse) way to have multiple parameter sets and still be able to call the function with no arguments?
It's because PowerShell can't figure out which parameter set you're trying to use. You can tell it what to default to in the CmdletBinding attribute.
function TwoSets{
[CmdletBinding(DefaultParameterSetName='A')]
param ( $NoSet,
[parameter(ParameterSetName = 'A')]$A,
[parameter(ParameterSetName = 'B')]$B )
process { $PSCmdlet.ParameterSetName }
}
Edit: I think None could be substituted for __AllParameterSets to achieve the goal of "creating a parameter set with no mandatory parameters at all". For more details see PowerShell/PowerShell#11237, #12619, and #11143
I don't see why the second case is any more ambiguous than the first. Why doesn't PowerShell bind to TwoSets using the "__AllParameterSets" parameter set?
PowerShell seems to use the __AllParameterSets parameter set as a default when there is only one user-named parameter set. On the other hand, when there is more than one user-named parameter set, PowerShell does not seem to consider any parameter set to be a default unless you specify it.
Is there a (terse) way to have multiple parameter sets and still be able to call the function with no arguments?
You can tell PowerShell to use __AllParameterSets as the default as follows:
function TwoSets{
[CmdletBinding(DefaultParameterSetName = '__AllParameterSets')]
param ( $NoSet,
[parameter(ParameterSetName = 'A', Mandatory = $true)]$A,
[parameter(ParameterSetName = 'B', Mandatory = $true)]$B )
process { $PSCmdlet.ParameterSetName }
}
Then invoking with no parameters, -A, or -B yields causes binding to the each of the three parameter sets as follows:
PS C:\> TwoSets
__AllParameterSets
PS C:\> TwoSets -A 'a'
A
PS C:\> TwoSets -B 'b'
B