Forgive me as I'm still learning powershell so this might be a silly question, but how do I add an options to a .ps1?
For example, I have currently have a script that downloads a file and runs it, but if it cant run it, it will look for the file locally and run it then. How do I separate these two so the user can pick either to download or just run locally?
e.g.:
'./script.ps1 local '
Will look for the file locally and run it
'./script.ps1 external'
will download the file and run it
I'm not sure if functions will be appropriate for this because the point of the script isn't to import it into the modules, I just want it so you run the .ps1.
At the top of your file add;
Param(
[Parameter(Position=1)][string]$option
)
Switch ($option)
{
'local' { RunLocal }
'other' { RunOther }
default { RunDefault }
}
Would look something like
Param(
[Parameter(Position=1)][string]$option
)
function RunLocal {
Write-Host "RunLocal"
}
function RunOther {
Write-Host "RunOther"
}
function RunDefault {
Write-Host "RunDefault"
}
Switch ($option)
{
'local' { RunLocal }
'other' { RunOther }
default { RunDefault }
}
If you need to constrain the values passed to a parameter to a fixed set of values, use the [ValidateSet(...)] parameter attribute:
[CmdletBinding()]
Param(
[ValidateSet('Local', 'External')]
[string] $Option = 'Local'
)
Switch ($Option)
{
'local' {
# ...
break
}
'external' {
# ...
break
}
}
The above defaults -Option (and thus parameter variable $Option) to 'Local', while allowing to pass either Local or External explicitly to -Option - no other values are permitted.
Related
Something is really weird with this language. I'm trying to execute a function and use its result value as condition. This is my code:
function Get-Platform()
{
# Determine current Windows architecture (32/64 bit)
if ([System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") -ne $null)
{
echo "x64"
return "x64"
}
else
{
echo "x86"
return "x86"
}
}
if (Get-Platform -eq "x64")
{
echo "64 bit platform"
}
if (Get-Platform -eq "x86")
{
echo "32 bit platform"
}
The expected output is this:
x64
64 bit platform
But the actual output is this:
64 bit platform
32 bit platform
What's going on here? How can this be fixed? I couldn't find any examples on the web that use functions inside an ifcondition. Is that possible at all in Powershell? I'm on Windows 7 with no special setup, so I have whatever PS version comes with it.
If you want to compare the return value of a function in a conditional, you must group the function call (i.e. put it in parentheses) or (as #FlorianGerhardt suggested) assign the return value of the function to a variable and use that variable in the conditional. Otherwise the comparison operator and the other operand would be passed as arguments to the function (where in your case they're silently discarded). Your function then returns a result that is neither "" nor 0 nor $null, so it evaluates to $true, causing both messages to be displayed.
This should do what you want:
...
if ( (Get-Platform) -eq 'x64' ) {
echo "64 bit platform"
}
...
BTW, you should avoid using separate if statements for conditions that are mutually exclusive. For a platform check an if..then..elseif
$platform = Get-Platform
if ($platform -eq "x64") {
...
} elseif ($platform -eq "x86") {
...
}
or a switch statement
Switch (Get-Platform) {
"x86" { ... }
"x64" { ... }
}
would be more appropriate.
I'd also avoid echoing inside the function. Just return the value and do any echoing that might be required in with the returned value. Anything echoed inside the function will also be returned to the caller.
One last note: personally I'd rather not rely on the existence of a particular folder or environment variable for determining the operating system architecture. Using WMI for this task deems me a lot more reliable:
function Get-Platform {
return (gwmi Win32_OperatingSystem).OSArchitecture
}
This function will return a string "32-Bit" or "64-Bit", depending on the operating system architecture.
I think you are comparing a function and not the function result. Also somehow the echo does not work as expected in a function. I usually use Write-Host.
Here is my solution to your problem:
function Get-Platform()
{
# Determine current Windows architecture (32/64 bit)
if ([System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") -ne $null)
{
Write-Host("x64")
return "x64"
}
else
{
Write-Host("x86")
return "x86"
}
}
$platform = Get-Platform
if ($platform -eq 'x64')
{
echo "64 bit platform"
}
if ($platform -eq 'x86')
{
echo "32 bit platform"
}
I am trying to write a simple wrapper that accept one parameter for the output.
This is how it looks now
function Get-data{
param (
[switch]$network,
[switch]$profile,
[switch]$server,
[switch]$devicebay
)
if ($network.IsPresent) { $item = "network"}
elseif ($profile.IsPresent) {$item = "profile"}
elseif ($server.IsPresent) {$item = "server"}
elseif ($devicebay.IsPresent){$item = "devicebay"}
$command = "show $item -output=script2"
}
Clearly this could be optimize but I am struggling to wrap my head around on how I can achieve it .Is there some easy way to ensure only single parameter is accepted and used without resorting to multiple elseif statements?
Also I would like to provide array of paramters instead doing it the way it is done at the moment.
Another thing you could do instead of all those switch parameters is to use a [ValidateSet]
function Get-Data{
[cmdletbinding()]
param(
[Parameter(Mandatory=$true)]
[ValidateSet('Network','Profile','Server','DeviceBay')]
[string]$Item
)
Switch ($Item){
'network' {'Do network stuff'}
'profile' {'Do profile stuff'}
'server' {'Do server stuff'}
'devicebay' {'Do devicebay stuff'}
}
}
Probably not the most elegant solution, but using parametersets makes powershell do some of the work for you:
#requires -version 2.0
function Get-data {
[cmdletbinding()]
param(
[parameter(parametersetname="network")]
[switch]$network,
[parameter(parametersetname="profile")]
[switch]$profile,
[parameter(parametersetname="server")]
[switch]$server,
[parameter(parametersetname="devicebay")]
[switch]$devicebay
)
$item = $PsCmdlet.ParameterSetName
$command = "show $item -output=script2"
}
This example will error out if you don't provide one of the switches, but you could probably provide an extra switch that does nothing or errors more gracefully if you want to account for that case...
You can add the [cmdletbinding()] keyword so you get $PSBoundParameters, and use that for a switch pipeline:
function Get-data{
[cmdletbinding()]
param (
[switch]$network,
[switch]$profile,
[switch]$server,
[switch]$devicebay
)
Switch ($PSBoundParameters.GetEnumerator().
Where({$_.Value -eq $true}).Key)
{
'network' { 'Do network stuff' }
'profile' { 'Do profile stuff' }
'server' { 'Do server stuff' }
'devicebay' { 'Do devicebay stuff' }
}
}
Since you want only one switch to be enabled, an enum might help you.
This way, you're not using a switch but a standard parameter - still, the user of the cmdlet can use TAB to autocomplete the values that may be entered.
Just set the type of the parameter to your enum.
Something is really weird with this language. I'm trying to execute a function and use its result value as condition. This is my code:
function Get-Platform()
{
# Determine current Windows architecture (32/64 bit)
if ([System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") -ne $null)
{
echo "x64"
return "x64"
}
else
{
echo "x86"
return "x86"
}
}
if (Get-Platform -eq "x64")
{
echo "64 bit platform"
}
if (Get-Platform -eq "x86")
{
echo "32 bit platform"
}
The expected output is this:
x64
64 bit platform
But the actual output is this:
64 bit platform
32 bit platform
What's going on here? How can this be fixed? I couldn't find any examples on the web that use functions inside an ifcondition. Is that possible at all in Powershell? I'm on Windows 7 with no special setup, so I have whatever PS version comes with it.
If you want to compare the return value of a function in a conditional, you must group the function call (i.e. put it in parentheses) or (as #FlorianGerhardt suggested) assign the return value of the function to a variable and use that variable in the conditional. Otherwise the comparison operator and the other operand would be passed as arguments to the function (where in your case they're silently discarded). Your function then returns a result that is neither "" nor 0 nor $null, so it evaluates to $true, causing both messages to be displayed.
This should do what you want:
...
if ( (Get-Platform) -eq 'x64' ) {
echo "64 bit platform"
}
...
BTW, you should avoid using separate if statements for conditions that are mutually exclusive. For a platform check an if..then..elseif
$platform = Get-Platform
if ($platform -eq "x64") {
...
} elseif ($platform -eq "x86") {
...
}
or a switch statement
Switch (Get-Platform) {
"x86" { ... }
"x64" { ... }
}
would be more appropriate.
I'd also avoid echoing inside the function. Just return the value and do any echoing that might be required in with the returned value. Anything echoed inside the function will also be returned to the caller.
One last note: personally I'd rather not rely on the existence of a particular folder or environment variable for determining the operating system architecture. Using WMI for this task deems me a lot more reliable:
function Get-Platform {
return (gwmi Win32_OperatingSystem).OSArchitecture
}
This function will return a string "32-Bit" or "64-Bit", depending on the operating system architecture.
I think you are comparing a function and not the function result. Also somehow the echo does not work as expected in a function. I usually use Write-Host.
Here is my solution to your problem:
function Get-Platform()
{
# Determine current Windows architecture (32/64 bit)
if ([System.Environment]::GetEnvironmentVariable("ProgramFiles(x86)") -ne $null)
{
Write-Host("x64")
return "x64"
}
else
{
Write-Host("x86")
return "x86"
}
}
$platform = Get-Platform
if ($platform -eq 'x64')
{
echo "64 bit platform"
}
if ($platform -eq 'x86')
{
echo "32 bit platform"
}
I'm trying to make use of the $args array with a pipeline parameter.
The function expects an arbitrary number of parameters (e.g. param0) following the first, pipelined parameter:
function rpt-params {
param (
[Parameter(ValueFromPipeline=$true,Position=0,Mandatory=$true)][CrystalDecisions.CrystalReports.Engine.ReportDocument]$reportDocument
)
try {
write-host "count: " $args.count
#TODO process args
}
catch [Exception] {
write-host $_.Exception
}
finally {
return $reportDocument
}
}
Attempts to call the function produce an error that reads "rpt-params : A parameter cannot be found that matches parameter name 'param0'.":
...
# syntax 0
rpt-params $rpt -param0 "mb-1" -param1 "me-1"
...
...
# syntax 1; explicitly naming the first parameter
rpt-params -reportDocument $rpt -param0 "mb-1" -param1 "me-1"
...
Is my syntax the issue or is it related to using a pipelined parameter?
Create another parameter, called it something like $rest and decorate it with [Parameter(ValueFromRemainingArguments = $true)].
When you use "[cmdletbinding()]" or "[Parameter()]", which is the case here, your Function turns into an Advanced Function. An Advanced Function can only take the Arguments that are specified under "Param" and no more. To make your Function act like before, like Keith recommends, you'll need to add [Parameter(ValueFromRemainingArguments = $true)]
For Example:
function rpt-params {
param (
[Parameter(ValueFromPipeline=$true,Position=0,Mandatory=$true)]
[CrystalDecisions.CrystalReports.Engine.ReportDocument]$reportDocument,
[Parameter(ValueFromRemainingArguments=$true)]$args
)
try {
write-host "count: " $args.count
#TODO Now args can have all remaining values
}
catch [Exception] {
write-host $_.Exception
}
finally {
return $reportDocument
}
}
I'm wanting to write a couple commands for the NuGet package manager console to insert Gists from GitHub. I have 4 basic commands
List-Gists 'user'
Gist-Info 'gistId'
Gist-Contents 'gistId' 'fileName'
Gist-Insert 'gistId' 'fileName'
All of my commands depend on a couple utility functions, and I'm struggling with whether they need to be global or not.
# Json Parser
function parseJson([string]$json, [bool]$throwError = $true) {
try {
$result = $serializer.DeserializeObject( $json );
return $result;
} catch {
if($throwError) { throw "ERROR: Parsing Error"}
else { return $null }
}
}
function downloadString([string]$stringUrl) {
try {
return $webClient.DownloadString($stringUrl)
} catch {
throw "ERROR: Problem downloading from $stringUrl"
}
}
function parseUrl([string]$url) {
return parseJson(downloadString($url));
}
Can I just have these utility functions outside of my global functions, or will I need to include them in each of the global functions definition scope somehow?
No they don't. From your init.ps1 you can import a powershell module that you wrote (psm1) file and moving forward, this will be the way we recommend adding methods to the console environment.
Your init.ps1 would look something like this:
param($installPath, $toolsPath)
Import-Module (Join-Path $toolsPath MyModule.psm1)
In MyModule.psm1:
function MyPrivateFunction {
"Hello World"
}
function Get-Value {
MyPrivateFunction
}
# Export only the Get-Value method from this module so that's what gets added to the nuget console environment
Export-ModuleMember Get-Value
You can get more information on modules here http://msdn.microsoft.com/en-us/library/dd878340(v=VS.85).aspx