Alias not shown in Manual - powershell-4.0

I add an alias to my PowerShell module, but it is not shown in Get-Help.
Here is an MWE:
#Require -Version 4.0
function global:Test-Syntax {
<#
.Synopsis
Cmdlet tests ValidateSet
#>
[CmdletBinding()]
# parameter check
param (
[ValidateSet("one", "two", "three")]
[string]$testparam
)
Write-Verbose "`$testparam: $testparam"
}
After that I define an alias in my profile:
New-Alias tstsyn Test-Syntax
Get-Help Test-Syntax doesn't show the alias.
In contrast: When I define the alias gh for Get-Help it is shown:
...
ALIASE
gh
...
Why are aliase in my own modules not shown in Get-Help?

Related

Powershell. Difference between piping commands and using Foreach-Object

Sorry if this question is already been answered, I could find similar questions but not the exact one I need to ask.
Let's take two examples:
1. Get-Process -name msedge,putty | Stop-Process
2. Get-Process -name msedge,putty | Foreach-Object {Stop-Process $_}
Both are doing the same operation. What about the methods used in each one? Are they the same in the sense that the first example just omits the Foreach-Object construction for the sake of code readability/aesthetics?
The first example requires the Cmdlet to support binding of the relevant parameters via the pipeline. In your case Stop-Process will bind the Process object from the pipeline to it's -InputObject parameter.
You can check that using get-help stop-process -Parameter * and see which parameters have "Accept pipeline input?" set to true.
In case a Cmdlet does not support the binding of the relevant parameters values you can wrap ForEach-Object around it, like you did in the second example. This way you use the automatic variable $_ to bind the current pipeline object (or information that you derive from it) "manually" to the corresponding parameter.
What approach should you use if a Cmdlet supports the binding of parameter values from the pipeline? That unfortunately depends. It is possible to write a Cmdlet that behaves differently, depending on how the parameter values are bound. Let me illustrate this point:
function Test-BindingFoo {
[CmdletBinding()]
param (
[Parameter(ValueFromPipeline)]
[string[]]
$InputParameter
)
begin {
Write-Host "[BEGIN]"
}
process {
foreach ($value in $InputParameter) {
Write-Host "The current value is: $value"
}
}
end {
Write-Host "[END]"
}
}
If you execute this Cmdlet using the pipeline binding the Begin block of the function is executed exactly once:
❯ "foo1", "foo2" | Test-BindingFoo
[BEGIN]
The current value is: foo1
The current value is: foo2
[END]
If you use ForEach-Object the Begin block is executed every time an object passes through the pipeline:
❯ "foo1", "foo2" | ForEach-Object { Test-BindingFoo $_ }
[BEGIN]
The current value is: foo1
[END]
[BEGIN]
The current value is: foo2
[END]
In well implemented Cmdlets the difference here should not matter. But I found it useful to be aware of what happens inside a Cmdlet when parameteres are passed in in the ways that we have discussed here.
You can do it this way too and use the kill method on the process object (operation statement):
Get-Process msedge,putty | Foreach-Object kill
# or
Get-Process msedge,putty | Foreach-Object -membername kill

Override functions multiple times for inheritant call stack logging

Let's assume the following CmdLets:
Write-MyColl
function Write-MyColl {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[psobject] $InputObject
)
$verb = Get-Command Write-Verbose
$fixedName = $PSCmdlet.MyInvocation.InvocationName
function Write-Verbose ($Object) {
& $verb "[$fixedName] $Object"
}
if ($InputObject.name) {
Write-Verbose "Writing `$InputObject's Name: `"$($InputObject.name)`""
}
}
New-MyColl
function New-MyColl {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string[]] $Names
)
$verb = Get-Command Write-Verbose
$fixedName = $PSCmdlet.MyInvocation.InvocationName
function Write-Verbose ($Object) {
& $verb "[$fixedName] $Object"
}
Write-Verbose "Names: $Names"
foreach ($name in $Names) {
#{
id = 123;
name = $name
} | Write-MyColl
}
}
As you can see New-MyColl overrides Microsoft.PowerShell.Utility\Write-Verbose with it's own Write-Verbose. This works perfectly and outputs:
VERBOSE: [New-MyColl] Names: [...]
Now, when Write-MyColl is invoked it should override Function:\Write-Verbose from New-MyColl and I want it to output something like:
VERBOSE: [New-MyColl] [Write-MyColl] Writing `$InputObject's Name: "[...]"
As you might already think, it doesn't work. Write-Verbose recursively calls itself until the end of days. I already tried local function definitions and local variables, but they won't be visible to the invoked CmdLet.
I think for my specific use-case, I will use Get-PsCallStack, however I'd love to see a solution to override overriden functions like described above.
What's hard for me to understand is, why the 2nd Write-Verbose calls itself, so $verb seemingly points to a function, which is not yet defined. Shouldn't it point to the prior defined function? I already tried to Remove-Item the funtion at the end of the script, but it didn't change anything.
Do you have any idea how to achieve this call-stack-behavior?
Thanks to PetSerAI's comment, I came to the following solution:
New-Item function::local:Write-Verbose -Value (
New-Module -ScriptBlock { param($verb, $fixedName, $verbose) } -ArgumentList #(
(Get-Command Write-Verbose),
$PSCmdlet.MyInvocation.InvocationName,
$PSCmdlet.MyInvocation.BoundParameters["Verbose"].IsPresent
)
).NewBoundScriptBlock{
param($Message)
if ($verbose) {
& $verb -Message "=>$fixedName $Message" -Verbose
} else {
& $verb -Message "=>$fixedName $Message"
}
} | Write-Verbose
The -Verbose Parameter isn't passed to the newly created function; that's why I added it to the -ArgumentList. I'm sure, it's not very pretty, but it does exactly what it should. Overwrite Write-Verbose for each call.
Now the output looks like:
VERBOSE: =>[New-MyColl] Names: [...]
VERBOSE: =>[New-MyColl]=>[Write-MyColl] Writing `$InputObject's Name: "[...]"

PowerShell two parameters either of them can be null but not both null

I'm wondering if this can be done at the parameter level rather in code:
I have 2 parameter: $School and $Class
Get-Students -School SchooName # Will give me all Students name in all classes in that School
Get-Students -Class ClassName # Will give me all Students name in that Class across all schools
Get-Students # Should return parameter level error.
I tried to use following:
function Get-Students{
param(
[parameter(ParameterSetName="School no null")]
[ValidateNotNullOrEmpty()]
[string]$School,
[parameter(ParameterSetName="School no null")]
[AllowNull()]
[string]$Class,
[parameter(ParameterSetName="Class no null")]
[AllowNull()]
[string]$School,
[parameter(ParameterSetName="Class no null")]
[ValidateNotNullOrEmpty()]
[string]$Class
)
}
But it doesn't work in this way...
Hope I explained this correctly. Or if there is no way can do this from parameter only inside of the code?
Thanks
River
Updated. The following commented code snippet is based on Add a parameter to multiple Parameter Sets article. Allows supplying -Class or -School parameter or both of them all together.
function Get-Students{
param(
[parameter(Mandatory=$false)][switch]$SomeThing, # any type instead of [switch]
# ↑↑↑ parameter(s) in all Parameter Sets
[parameter(Mandatory=$true, ParameterSetName="School No Null")]
[parameter(Mandatory=$true, ParameterSetName="Class And School")]
[ValidateNotNullOrEmpty()]
[string]$School,
[parameter(Mandatory=$true, ParameterSetName="Class No Null")]
[parameter(Mandatory=$true, ParameterSetName="Class And School")]
[ValidateNotNullOrEmpty()]
[string]$Class
)
Write-Host $PsCmdlet.ParameterSetName -ForegroundColor Cyan
switch ($PsCmdlet.ParameterSetName)
{
"School No Null" { "$School/*,$($SomeThing.IsPresent)"; break}
"Class No Null" { "*/$Class,$($SomeThing.IsPresent)"; break}
"Class And School" { "$School/$Class,$($SomeThing.IsPresent)"; break}
}
}
Get-Students -Class "A"
Get-Students -School "West" -SomeThing
Get-Students -Class "A" -School "West"
### errors raised:
# Get-Students
### Parameter set cannot be resolved using the specified named parameters.
### + FullyQualifiedErrorId : AmbiguousParameterSet,Get-Students
# Get-Students -Class ""
# Get-Students -School ""
### Cannot validate argument on parameter '…'. The argument is null or empty.
### + FullyQualifiedErrorId : ParameterArgumentValidationError,Get-Students
Original answer (a code snippet based on PowerShell V2: ParameterSets article from PowerShell Team Blog and on MSDN help article) does not allow supplying both -Class and -School parameters all together:
function Get-Students{
param(
[parameter(Mandatory=$true, ParameterSetName="School No Null")][ValidateNotNullOrEmpty()]
[parameter(Mandatory=$false, ParameterSetName="Class No Null")] #[AllowNull()]
[string]$School,
[parameter(Mandatory=$true, ParameterSetName="Class No Null")] [ValidateNotNullOrEmpty()]
[parameter(Mandatory=$false, ParameterSetName="School No Null")]#[AllowNull()]
[string]$Class
)
Write-Host $PsCmdlet.ParameterSetName -ForegroundColor Cyan
switch ($PsCmdlet.ParameterSetName)
{
"School No Null" { $School; break}
"Class No Null" { $Class; break}
}
}
### valid calls:
Get-Students -Class "A"
Get-Students -School "West"
### errors raised:
### Parameter set cannot be resolved using the specified named parameters.
# Get-Students
# Get-Students -Class "A" -School "West"
### The argument is null or empty.
# Get-Students -Class ""
# Get-Students -School ""
You can test them in code instead of trying to stuff this check into parameter set.
if ($School -eq $null -and $Class -eq $null) {throw "School and Class must not be null together!"}

How can i use a function inside the same script file without getting $args ending up quoted as i. e. "arg1 arg2"

I have a powershell (v2.0) function:
function mvn{
param(
[parameter(Position=0, ValueFromRemainingArguments=$true)]
$args
)
<# calling the realmaven.bat file here with the args #>
"$env:M2_HOME\bin\mvn.bat" $args
}
Now I would like to use another function in this scripts file using the former:
function mvn-failsafe-debug {
param (
[string] $Dmaven_failsafe_debug="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_shmem,server=y,address=Maven,suspend=n",
[parameter(Position=0, ValueFromRemainingArguments=$true)]
$args
)
<#
calling the predefined mvn function above
#>
mvn `-Dmaven.failsafe.debug=$Dmaven_failsafe_debug $args
}
The generated command when using as mvn-failsafe-debug clean install on the powershell prompt now is something like:
realmaven.bat ... "clean install"
but I need it to be
realmaven.bat ... clean install
How to achieve that?
Not sure if this is the entire source of you woes but for starters don't name a variable $args. This is an automatic variable in PowerShell. You can look at about_Automatic_Variables to see what I mean.
If nothing else you should rename that line in param. I simply changed the variable name to $remains.
The in that same regard you could change the calling function to something like this.
function mvn{
param(
[parameter(Position=0, ValueFromRemainingArguments=$true)]
$remains
)
Write-Host $remains
Start-Process "$($env:M2_HOME)\bin\mvn.bat" -ArgumentList ($remains -join "")
}
You have two options. You can either call function mvn with an array of arguments instead of a string, or you can split the string.
So, either leave the function as is and use
mvn-failsafe-debug clean, install
Or change it to
function mvn {
param(
[parameter(Position=0)]
[string] $arguments
)
Start-Process "$($env:M2_HOME)\bin\mvn.bat" -ArgumentList ($arguments -split " ")
}
#Swoogan, your $(arguments -split " ") finally gave what I needed.
For comprehension the solution I use for now listed as follows (as a simple use case mvn-eval as a more complex one mvn-surefire-debug):
function mvn {
param(
[parameter(Position=0)]
[string] $arguments
)
& "$($env:M2_HOME)\bin\mvn.bat" ($arguments -split " ")
}
function mvn-eval {
param (
[string] $Dexpression="basedir",
[parameter(Position=0, ValueFromRemainingArguments=$true)]
$args
)
mvn "help:evaluate $args"
}
function mvn-surefire-debug {
param (
[string] $Dmaven_surefire_debug="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_shmem,server=y,address=Maven,suspend=n"
, [parameter(Position=0, ValueFromRemainingArguments=$true)]
$remains
)
mvn """`-Dmaven.surefire.debug=$Dmaven_surefire_debug"" $remains"
}

Powershell: how to implement standard switches?

For things like -WhatIf, we have $PSCmdlet.ShouldProcess() given to us by the [CmdletBinding] attribute. Are there other such tools or practices for implementing common command line arguments such as -Verbose, -Debug, -PassThru, etc?
Write-Debug and Write-Verbose handle the -Debug and -Verbose parameters automatically.
-PassThru isn't technically a common parameter, but you can implement it like:
function PassTest {
param(
[switch] $PassThru
)
process {
if($PassThru) {$_}
}
}
1..10|PassTest -PassThru
And this is an example of using your function's PassThru value on a cmdlet:
function Add-ScriptProperty {
param(
[string] $Name,
[ScriptBlock] $Value,
[switch] $PassThru
)
process{
# Use ":" to explicitly set the value on a switch parameter
$_| Add-Member -MemberType ScriptProperty -Name $Name -Value $Value `
-PassThru:$PassThru
}
}
$calc = Start-Process calc -PassThru|
Add-ScriptProperty -Name SecondsOld `
-Value {((Get-Date)-$this.StartTime).TotalSeconds} -PassThru
sleep 5
$calc.SecondsOld
Have a look at Get-Help about_CommonParameters for more information.

Resources