Is there an easy way to parse the params from a powershell script file
param(
[string]$name,
[string]$template
)
I have started reading the file and wondered if there is a better way, maybe by a help/man command?
class PowerShellParameter {
public string Name;
public string Type;
public string Default;
}
string[] lines = File.ReadAllLines(path);
bool inparamblock = false;
for (int i = 0; i < lines.Length; i++) {
if (lines[i].Contains("param")) {
inparamblock = true;
} else if (inparamblock) {
new PowerShellParameter(...)
if (lines[i].Contains(")")) {
break;
}
}
}
There are at least two possibilies. First one (imho better): use Get-Command:
# my test file
#'
param(
$p1,
$p2
)
write-host $p1 $p2
'# | Set-content -path $env:temp\sotest.ps1
(Get-Command $env:temp\sotest.ps1).parameters.keys
For all members look at
Get-Command $env:temp\sotest.ps1 | gm
#or
Get-Command $env:temp\sotest.ps1 | fl *
The other (harder way) is to use regular expression
[regex]::Matches((Get-Help $env:temp\sotest.ps1), '(?<=\[\[-)[\w]+') | select -exp Value
I like the solution with Get-Command proposed by #stej. Unfortunately it does not work if script parameters have explicit types specified and an assembly of such a type is not yet loaded into the session. That is why I still use this script: Get names of script parameters
I'm not really sure what you're after, is it documenting your scripts? In that case have a look at Get-Help about_Comment_Based_Help. It will tell you how to do that, and after that you can use Get-Help on your script/module.
If you're after more strict parameter handling, take a look at about_functions_advanced_parameters and about_functions_cmdletbindings on how to better structure parameters. For example,
[Parameter(Position=0,Mandatory=$true,HelpMessage='Enter
architecture("OSX","WinXP","Win7","Linux")')]
[ValidateSet("OSX","WinXP","Win7","Linux")]
[string]$architecture
will make that parameter mandatory, read from position 0 of the command, allow only a value from the given set, and give a brief help message when asking for input if that parameter was not given.
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'm a beginner at Powershell and am struggling to understand some syntax from some code I found on Github. I've read the docs on Powershell assignment, and on switch statements, and can't understand what is going on with the = $Yes and = $No in this code snippet:
Switch ($Prompt3) {
Yes {
Stop-EdgePDF
Write-Output "Edge will no longer take over as the default PDF viewer."; = $Yes
}
No {
= $No
}
}
I haven't been able to find any references to this kind of syntax, and it doesn't seem to do anything in the script. So why is it there?
UPDATE: This issue has been resolved.
Looks to me like the variable name that was getting the assignment was deleted in a change back in August.
$PublishSettings = $Yes
Was changed to:
= $Yes
And:
$PublishSettings = $No
Was changed to:
= $No
Looks like poor search and replace.
I've created an issue for the problem at GitHub.
There are many characters that are valid in a function (or variable) name; this includes the = symbol. What you're observing is a function or alias.
Examples:
# standard function
function =
{
return $args
}
# accessing the function: drive
${Function:=} = {
return $args
}
# defining a new alias
New-Alias -Name = -Value Get-Variable
# using the Alias attribute
function Test-Thing
{
[Alias('=')]
param()
return $args
}
As we know, PowerShell has wacky return semantics.
Function return value in PowerShell shows there are two main ideas to wrap my head around:
All output is captured, and returned
The return keyword just indicates a logical exit point
Even things like reserving variables in outer scopes cause output, like [boolean]$isEnabled. Another good one is $someCollection.Add("toto") which spits the new collection count. Even Append() function causes output.
For example :
Function MyFunc {
$res1 = new-object System.Text.StringBuilder
$res1.Append("titi");
$res2 = "toto"
return $res2
}
$s = MyFunc
Write-Host $s
The output is : titi toto.
The expected output should be toto.
How to use a powershell function to return the expected value? (at least when viewed from a more traditional programming perspective)
Change
$res1.Append("titi");
to
$res1.Append("titi") | Out-Null
because the function returns every output which otherwise would be visible in the console.
if by using 'toto' you are trying to understand if your function succeeded, you could do
Function MyFunc {
$res1 = new-object System.Text.StringBuilder
$res1.Append("titi") | Out-Null
return $?
}
"$?" returns a boolean if the previous command succeeded (true) or failed (false). so externally it would look like
$s = MyFunc
if ($s) {
Write-Host "successful" -Foregroundcolor Green
}
else {
Write-Error "unsuccessful"
}
When PowerShell was being developed, the team wanted to make it simple to use. But, it was confusing to people who know return from other languages. The implementation in classes is an attempt to rectify that mistake.
The return keyword works very differently in methods in PowerShell classes. It works like the return statements in other languages.
In a class method, the return keyword:
Exits the current scope.
Returns the associated object (return ).
Returns only the associated object.
The object that Return returns must match the return type of the method.
It is consistent with the return keyword and analogous keywords in other languages.
class ClassMyFunc
{
[string] MyFunc
{
$res1 = new-object System.Text.StringBuilder
$res1.Append("titi")
$res2 = "toto"
return $res2
}
}
$cmf = New-Object -TypeName ClassMyFunc
$cmf.MyFunc()
The output is : toto, as expected.
Using classes solved my problem, without having to search all functions returning a value in the console and piping it to Out-Null (as suggested by #TobyU).
I'm trying to pass an array of custom objects to a function for further processing of these objects.
Here's the function where I create my custom object array:
Function GetNetworkAdapterList
{
# Get a list of available Adapters
$hnet = New-Object -ComObject HNetCfg.HNetShare
$netAdapters = #()
foreach ($i in $hnet.EnumEveryConnection)
{
$netconprop = $hnet.NetConnectionProps($i)
$inetconf = $hnet.INetSharingConfigurationForINetConnection($i)
$netAdapters += New-Object PsObject -Property #{
Index = $index
Guid = $netconprop.Guid
Name = $netconprop.Name
DeviceName = $netconprop.DeviceName
Status = $netconprop.Status
MediaType = $netconprop.MediaType
Characteristics = $netconprop.Characteristics
SharingEnabled = $inetconf.SharingEnabled
SharingConnectionType = $inetconf.SharingConnectionType
InternetFirewallEnabled = $inetconf.InternetFirewallEnabled
SharingConfigurationObject = $inetconf
}
$index++
}
return $netAdapters
}
Then in my main code I call above function like this:
$netAdapterList = GetNetworkAdapterList
The $netAdapterList returns the expected data, and I can do stuff like:
$netAdapterList | fl Name, DeviceName, Guid, SharingEnabled
So far so good.
Now I want to call a function passing in the $netAdapterList
I've created a dummy function like this:
Function ShowAdapters($netAdapterListParam)
{
$netAdapterListParam | fl Name, DeviceName, Guid, SharingEnabled
}
And when I invoke it like this:
ShowAdapters $netAdapterList
Nothing gets printed out.
I've tried changing the function's signature but still no luck:
Function ShowAdapters([Object[]]$netAdapterListParam)
Function ShowAdapters([Object]$netAdapterListParam)
Function ShowAdapters([PSObject[]]$netAdapterListParam)
Function ShowAdapters([array]$netAdapterListParam)
Anybody knows what I'm doing wrong? How can I get to my custom objects inside the function?
Thanks for your reply #Christian. Tried your steps, copy pasting bits and pieces into the shell and it indeed worked. However If i run the full .ps1 script is not printing out anything.
I've run the script in Powershell IDE setting breakpoints inside the ShowAdapters function, and $netAdapterListParam has indeed the expected custom objects array I'm passing in, so I've narrowed down the issue to be in the FL commandlet.
For some reason $netAdapterList | fl Name, DeviceName, Guid, SharingEnabled did not work for me, so I ended up using the following instead:
$formatted = $netAdapterListParam | fl Name, DeviceName, Guid, SharingEnabled | Out-String
Write-Host $formatted
That did the trick and the 4 properties were printed on the screen.
Lessons learned:
1) The Powershell IDE built into Win7 can be a very useful tool for debugging scripts
2) Format-List can be quirky when formatting custom objects, so Out-String was required.