有没有办法以仅在将参数传递给别名时才运行的方式为 cmdlet 创建别名? [英] 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?
问题描述
我正在尝试 创建Get-Command cmdlet,如果我不发送任何参数,它就不会运行(因为如果它在没有参数它输出所有可用的命令).
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).
我知道这可以使用函数来完成,但我想保留选项卡完成功能,而不必编写一个可放入我的 $PROFILE.
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.
推荐答案
你不能用别名来做,因为PowerShell别名只能引用另一个命令名称或路径,因此既不能包含参数,也不能包含自定义逻辑.
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.
因此您确实需要一个函数,但它可以是一个简短而简单的:
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." } }
请注意,虽然传递 -?
以显示 Get-Command
的帮助确实有效,但参数的 Tab 补全无效.
Note that while passing -?
for showing Get-Command
's help does work, tab completion of arguments does not.
为了获得 Tab 补全,您需要编写一个包装器(代理)函数或至少复制 Get-Command
的参数声明 - 然后确实使函数定义变得可观.
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 sizable.
如果只关心 $PROFILE
文件本身的大小,你可以写一个代理 script 代替 - which.ps1
-你也可以只用 which
调用它,假设你把它放在 $env:Path
[1] 中列出的目录之一中;见下一节.
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
[1]; see next section.
定义包装(代理)函数或脚本 是一项重要的任务,但允许您实现一个强大的包装器,该包装器支持制表符完成甚至转发到原始命令的帮助.
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.
注意:
错误警报:正如 zett42 指出的那样,从 PowerShell [Core] 7.1,
System.Management.如果目标命令是(高级)函数或脚本,Automation.ProxyCommand.Create
忽略包含动态参数;但是,编译后的cmdlet不受到影响;请参阅 GitHub 问题 #4792 和 这个答案 解决方法.
Bug alert: As zett42 points out, as of PowerShell [Core] 7.1,
System.Management.Automation.ProxyCommand.Create
neglects to include dynamic parameters if the target command is an (advanced) function or script; however, compiled cmdlets are not affected; see GitHub issue #4792 and this answer for a workaround.
为简单起见,下面创建一个包装脚本,which.ps1
,并将其保存在当前目录中.如上所述,如果您将它放在 $env:PATH
中列出的目录之一中,您将能够像 which
一样调用它.
For simplicity, the following creates a wrapper script, which.ps1
, and saves it in the current directory. As stated, if you place it in one of the directories listed in $env:PATH
, you'll be able to invoke it as just which
.
下面的代码可以很容易地改写来创建一个包装函数:只需获取下面的 $wrapperCmdSource
变量的内容并将其包含在 function which { ... }
.
The code below can easily be adapted to create a wrapper function instead: simply take the contents of the $wrapperCmdSource
variable below and enclose it in function which { ... }
.
自 PowerShell Core 7.0.0-preview.5 起,自动生成的代码存在一些问题,可能会也可能不会影响您;它们将在某个时候修复;要了解更多信息并了解如何手动更正它们,请参阅 GitHub 问题 #10863.
As of PowerShell Core 7.0.0-preview.5, there are some problems with the auto-generated code, which may or may not affect you; they will be fixed at some point; to learn more and to learn how to manually correct them, see GitHub issue #10863.
# 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
注意:
即使
System.Management.Automation.ProxyCommand.Create
需要System.Management.Automation.CommandMetadata
实例来识别目标命令,System.Management.Automation.CommandInfo
实例输出由Get-Command
可以按原样使用.
重新基于评论的帮助:默认情况下,代理功能只是转发原始cmdlet的帮助;但是,您可以选择传递一个字符串作为基于注释的帮助作为第二个参数.
Re comment-based help: By default, the proxy function simply forwards to the original cmdlet's help; however, you can optionally pass a string to serve as the comment-based help as the 2nd argument.
- 通过将
[System.Management.Automation.ProxyCommand]::GetHelpComments()
与Get-Help
的输出结合使用,您可以从原始命令的帮助并修改它:[System.Management.Automation.ProxyCommand]::GetHelpComments((Get-Help Get-Command))
- By using
[System.Management.Automation.ProxyCommand]::GetHelpComments()
in combination with output fromGet-Help
, you could start with a copy of the original command's help and modify it:[System.Management.Automation.ProxyCommand]::GetHelpComments((Get-Help Get-Command))
您现在拥有一个功能齐全的 which.ps1
包装器脚本,其行为类似于 Get-Command
本身.
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.
具体来说,将以下命令作为 begin { ... }
块中的第一条语句:
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.
...
其他常见的自定义类型是:
从包装器中删除参数,只需删除参数声明.
Removing parameters from the wrapper, by simply removing the parameter declaration.
通过修改 begin
块中的以下行,向包装命令的调用添加附加参数:
Adding additional parameters to the invocation of the wrapped command, by modifying the following line in the begin
block:
# Add parameters, as needed.
$scriptCmd = { & $wrappedCmd @PSBoundParameters }
在将管道输入传递给包装命令之前对其进行预处理,方法是自定义 process
块并将 $_
替换为以下行中的预处理输入:
Preprocessing pipeline input before passing it to the wrapped command, by customizing the process
block and replacing $_
with your preprocessed input in the following line:
# Replace $_ with a preprocessed version of it, as needed.
$steppablePipeline.Process($_)
[1] 给 Linux 用户的警告:由于 Linux 文件系统区分大小写敏感,调用您的脚本将无法正常工作-不敏感,命令通常在 PowerShell 中工作的方式.例如,如果您的脚本文件名是 Get-Foo.ps1
,则只有 Get-Foo
- 使用完全相同的情况 - 会起作用,而 get-foo 不会
,例如.
[1] 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.
这篇关于有没有办法以仅在将参数传递给别名时才运行的方式为 cmdlet 创建别名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!