Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
774 views
in Technique[技术] by (71.8m points)

terminal - What is the simplest way to make Alias in powershell?

I was wondering if there's any simple way to make aliases for powershell like cmd. For example: In cmd, doskey art=php artisan $* where $* is optional. Currently, I'm using the following alias in powershell.

function runArtisanCommand
{
    param(
        [Parameter(Mandatory=$false, Position = 0, ValueFromRemainingArguments = $true)]
        $command
    )
    php artisan $command
}

Set-Alias art runArtisanCommand

This works somewhat but don't take flags. For example: I can't write art -h or art route:list -c. In art -h command, it prints the output of php artisan and don't read flag at all but in art route:list -c command, it errors out with.

runArtisanCommand : Missing an argument for parameter 'command'. Specify a parameter of type 'System.Object' and try again.
At line:1 char:16
+ art route:list -c
+                ~~
    + CategoryInfo          : InvalidArgument: (:) [runArtisanCommand], ParameterBindingException
    + FullyQualifiedErrorId : MissingArgument,runArtisanCommand

I would love a simpler solution than this. Thanks in advance.

question from:https://stackoverflow.com/questions/65935207/what-is-the-simplest-way-to-make-alias-in-powershell

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

The simplest and most convenient way to pass unknown arguments through is by spatting the automatic $args array - as @args - in a simple function or script (one that neither uses a [CmdletBinding()] nor [Parameter()] attributes):

# Note: @args rather than $args makes the function work with named 
#       arguments for PowerShell commands too - see explanation below.
function runArtisanCommand { php artisan @args } 

# As in your question: Define alias 'art' for the function
# Note: Of course, you could directly name your *function* 'art'.
#       If you do want the function to have a longer name, consider one
#       that adheres to PowerShell's Verb-Noun naming convention, such as
#       'Invoke-ArtisanCommand'.
Set-Alias art runArtisanCommand

As an aside: Since the target executable, php, is neither quoted nor specified based on a variable or expression, it can be invoked as-is; otherwise, you would need &, the call operator - see this answer for background information.


As for what you tried:

The problem was that use of -c as a pass-through argument only works if you precede it with --:

# OK, thanks to '--'
art -- route:list -c

-- tells PowerShell to treat all remaining arguments as unnamed (positional) arguments, instead of trying to interpret tokens such as -c as parameter names.

Without --, -c is interpreted as referring to your -command parameter (the parameter you declared as $command with ValueFromRemainingArguments = $true), given that PowerShell allows you to specify name prefixes in lieu of full parameter names, as long as the given prefix is unambiguous.

Because a parameter of any type other than [switch] requires an associated argument, -c (aka -command) failed with an error message to that effect.

You could have avoided the collision by naming your parameter so that it doesn't collide with any pass-through parameters, such as by naming it -_args (with parameter variable $_args):

function runArtisanCommand
{
    param(
        # Note: `Mandatory = $false` and `Position = 0` are *implied*.
        [Parameter(ValueFromRemainingArguments)]
        $_args
    )
    php artisan @_args
}

However, given that use of a [Parameter()] attribute implicitly makes your function an advanced function, it invariably also accepts common parameters, such as -ErrorAction, -OutVariable, -Verbose... - all of which can be passed by unambiguous prefix / short alias too; e.g., -outv for -OutVariable, or alias -ea for ErrorAction; collisions with them cannot be avoided.

Therefore, intended pass-through arguments such as -e still wouldn't work:

# FAILS, because -e ambiguously matches common parameters -ErrorAction 
# and -ErrorVariable.
PS> art router:list -e
Parameter cannot be processed because the parameter name 'e' is ambiguous.
Possible matches include: -ErrorAction -ErrorVariable.

Again, -- is needed:

# OK, thanks to '--'
art -- router:list -e

Summary:

  • Especially for functions wrapping calls to external programs, such as php.exe, using a simple function with @args, as shown at the top, is not only simpler, but also more robust.

  • For functions wrapping PowerShell commands (with explicitly declared parameters):

    • a simple function with @args works too,
    • but if you also want support for tab-completion and showing a syntax diagram with the supported parameters, by passing -?, or via Get-Help, consider defining an (invariably advanced) proxy (wrapper) function via the PowerShell SDK - see below.

Optional background information: Pass-through arguments in PowerShell

As Mathias R. Jessen points out, the simplest way to pass (undeclared) arguments passed to a function or script through to another command is to use the automatic $args variable, which is an automatically populated array of all the arguments passed to a simple function or script (one that isn't advanced, through use of the [CmdletBinding()] and/or [Parameter()] attributes).

As for why @args (splatting) rather than $args should be used:

  • Using $args as-is in your wrapper function only works for passing positional arguments through (those not prefixed by the parameter name; e.g., *.txt), as opposed to named arguments (e.g., -Path *.txt).

  • If the ultimate target command is an external program (such as php.exe in this case), this isn't a problem, because PowerShell of necessity then treats all arguments as positional arguments (it cannot know the target program's syntax).

  • However, if a PowerShell command (with formally declared parameters) is ultimately called, only splatting the $args array - which syntactically means us of @args instead - supports passing named arguments through.[1]

Therefore, as a matter of habit, I suggest always using @args in simple wrapper functions, which equally works with external programs.[2]

To give an example with a simple wrapper function for Get-ChildItem:

# Simple wrapper function for Get-ChildItem that lists recursively
# and by relative path only.
function dirTree {
  # Use @args to make sure that named arguments are properly passed through.
  Get-ChildItem -Recurse -Name @args 
}

# Invoke it with a *named* argument passed through to Get-ChildItem
# If $args rather than @args were used inside the function, this call would fail.
dirTree -Filter *.txt

Using a proxy function for more sophisticated pass-through processing:

The use of @args is convenient, but comes at the expense of not supporting the following:

  • tab-completion, given that tab-completion only works with formally declared parameters (typically with a param(...) block).

  • showing a syntax diagram with the supported parameters, by passing -?, or via Get-Help

To overcome these limitations, the parameter declarations of the ultimate target command must be duplicated in the (then advanced) wrapper function; while that is cumbersome, PowerShell can automate the process by scaffolding a so-called proxy (wrapper) function via the PowerShell SDK - see this answer.

Note:

  • With respect to common parameters such as -ErrorAction, it is the proxy function itself that (automatically) processes them, but that shouldn't make a difference to the caller.

  • Scaffolding a proxy function only works with PowerShell commands, given that PowerShell has no knowledge of the syntax of external programs.

    • However, you can manually duplicate the parameter declarations of the external target program.

[1] Note that the automatic $args array has built-in magic to support this; passing named arguments through with splatting is not supported with a custom array and requires use of a hash table instead, as discussed in the help topic about splatting linked to above.

[2] In fact, only @args also supports the correct interpretation of --%, the stop-parsing symbol.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...