powershellaliaswrapper

Is there a way to create an alias to a cmdlet in a way that it only runs if arguments are passed to the alias?


I am trying to create an alias (named which) of the Get-Command cmdlet in a way that it doesn't run if I'm not sending any arguments (because if it's run without arguments it outputs all the commands that are available).

I know this can be done using a function but I would like to keep the tab completion functionality without having to write a sizeable function that is to be placed into my $PROFILE.

In short, I only want the alias to work if it is being passed arguments.


Solution

  • You can't do it with an alias, because PowerShell aliases can only refer to another command name or path, and can therefore neither include arguments nor custom logic.[1]
    In other words: they're just alternative names for existing commands.

    Therefore you do need a function, but it can be a short and simple one:

    function which { 
      if ($args.Count) { Get-Command @args } else { Throw "Missing command name." } 
    }
    

    Note:

    In order to get tab completion as well, you'll need to write a wrapper (proxy) function or at least replicate Get-Command's parameter declarations - which then does make the function definition more complex and lengthier; see next section.

    If the concern is just the size of the $PROFILE file itself, you can write a proxy script instead - which.ps1 - which you can invoke with just which as well, assuming you place it in one of the directories listed in $env:Path[2]; see next section.


    Defining a wrapper (proxy) script or function:

    Defining a wrapper (proxy) function or script is a nontrivial undertaking, but allows you to implement a robust wrapper that supports tab completion and even forwarding to the original command's help.

    Note:

    # Create the wrapper scaffolding as source code (outputs a single [string])
    $wrapperCmdSource = 
      [System.Management.Automation.ProxyCommand]::Create((Get-Command Get-Command))
      
    # Write the auto-generated source code to a script file
    $wrapperCmdSource > which.ps1
    

    Note:

    You now have a fully functional which.ps1 wrapper script that behaves like Get-Command itself.

    You can invoke it as follows:

    ./which    # Same as: Get-Command; tab completion of parameters supported.
    ./which -? # Shows Get-Command's help.
    

    You can now edit the script file to perform the desired customization.

    Note: The auto-generated source code contains a lot of boilerplate code; however, typically only one or two places need tweaking to implement the custom functionality.

    Specifically, place the following command as the first statement inside the begin { ... } block:

    if (-not $MyInvocation.ExpectingInput -and -not ($Name -or $CommandType -or $Module -or $FullyQualifiedModule)) {
      Throw "Missing command name or filter."
    }
    

    This causes the script to throw an error if the caller didn't provide some way of targeting a specific command or group of commands, either by direct argument or via the pipeline.

    If you invoke the modified script without arguments now, you should see the desired error:

    PS> ./which.ps1
    Missing command name or filter.
    ...
    

    Other common types of customizations are:

    For an example of a complete implementation of a proxy function, see this answer.


    [1] GitHub issue #12962 is a proposal to someday support Bash-style aliases in PowerShell too, i.e. to support including arguments in the call to the aliased command. Properly implemented, such enhanced aliases could automatically support pipeline input too as well as tab-completion.

    [2] Caveat for Linux users: since the Linux file-system is case is case-sensitive, invocation of your script won't work case-insensitively, the way commands normally work in PowerShell. E.g., if your script file name is Get-Foo.ps1, only Get-Foo - using the exact same case - will work, not also get-foo, for instance.