-
Notifications
You must be signed in to change notification settings - Fork 8.1k
Get-Command: Specifying an alias and -Syntax returns the aliased commands syntax #10784
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Get-Command: Specifying an alias and -Syntax returns the aliased commands syntax #10784
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make the tests reliable we need to load Microsoft.PowerShell.Management module.
Please add this in BeforeAll block
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I've added it in the right place, I couldn't see any other examples of how to do it so figured a simple Import-Module Microsoft.PowerShell.Management would be enough.
|
@daxian-dbw It seems there is a problem with TestAppDomainProcessExitEvenHandlerNotLeaking test. Could you please look? Description: PSTests.Sequential.RunspaceTests PSTests.Sequential.RunspaceTests PSTests.Sequential.RunspaceTests PSTests.Sequential.RunspaceTests
Name: TestAppDomainProcessExitEvenHandlerNotLeaking TestRunspaceWithPowerShellAndInitialSessionState TestRunspaceWithPipeline TestRunspaceWithPowerShell
message:
Test-XUnitTestResults : Cannot validate argument on parameter 'message'. The argument is null or empty. Provide an
argument that is not null or empty, and then try the command again.
At D:\a\_temp\85b7acd9-47dd-489a-a2cb-ec41b0000555.ps1:5 char:1
+ Test-XUnitTestResults -TestResultsFile $xUnitTestResultsFile
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Test-XUnitTestResults], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Test-XUnitTestResults |
You don't need to change the i.e. When you discover that it is an alias, store the alias name, and then later when you retrieve the command syntax, replace the command name with the alias name in the output, while also pre-pending that output with a comment showing the alias to command relationship. This will be very helpful when an alias references a script path, because you'll see the alias in the output syntax (which is the command you requested syntax for), while also seeing that the command you're requesting syntax for is actually an alias for another command (good for awareness/educational purposes). I'd really like to see the output from this match something more along these lines: # First, create a script file
@'
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
[ValidateNotNullOrEmpty()]
[string[]]
$Name
)
process {
"Processing ${Name}"
}
'@ | out-File ${env:USERPROFILE}\Test-GcmSyntax.ps1
# Now set up an alias for that file
new-alias tgs ${env:USERPROFILE}\Test-GcmSyntax.ps1
# Get the command syntax using the alias
gcm -syntax tgsDesired output: To make that work, you can inject the alias definition at the top (wrapping a script path in quotes if it has spaces), and then use regex to replace If we don't agree on showing the alias definition at the top, at a minimum I would expect the syntax to show the alias name instead of the command/script it points to. As an alternative, we could leave the syntax as-is, and just show the alias note at the top so that users see first that they asked for syntax for an alias, and then they see the syntax for the command that is being aliased. As another alternative, we could show the alias details in verbose output instead, so that users only see it when invoking the command with |
|
I've been digging into doing that a bit and I've managed to get this so far: It doesn't bring back the full path for a script but I'll see what I can do about that. I do like the extra information that shows what the alias is for in a more clear way than just replacing the name in the syntax output. |
|
After a bit more fiddling around I've got it working with external scripts, I need to add some test cases for this and need to play around with it and native commands to see how that comes out. |
|
You can probably relay the information that it's an alias a bit more succinctly, similarly to how I do think having a line break after the "hey this is an alias" line is a good idea too, it helps clarify that the information being given is in two parts. 🙂 |
That's what I'm thinking, it'll take a bit more conditional formatting as the syntax for a command already includes the leading line breaks but I can add that in easily enough. |
Except with With |
|
Something shorter would be good though, especially if it doesn't need to be localized. Maybe something like this:
You wouldn't need to quote the right-hand side of the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And how does output look with the replace?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I see you checking for wildcards. Is the logic smart enough to show the alias properly if a wildcard search finds commands that are aliases and outputs the syntax for them? There should probably be a specific test just for that scenario to make sure it is covered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For scripts it looks like this:
PS> gcm tgs -syn
tgs -> C:\Users\Chris\Test-GcmSyntax.ps1
tgs [-Name] <string[]> [<CommonParameters>]
For commands it's:
PS> gcm del -syn
del -> Remove-Item
del [-Path] <string[]> [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Force] [-Credential <pscredential>]
[-WhatIf] [-Confirm] [-Stream <string[]>] [<CommonParameters>]
del -LiteralPath <string[]> [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Force] [-Credential <pscredential>] [-WhatIf] [-Confirm] [-Stream <string[]>] [<CommonParameters>]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also I see you checking for wildcards. Is the logic smart enough to show the alias properly if a wildcard search finds commands that are aliases and outputs the syntax for them? There should probably be a specific test just for that scenario to make sure it is covered.
Not right now, I need to add something in for that.
With the codefactor tests complaining about it being too complex I'll break out this into it's own method as it'll need to handle this case too and that'll make it more difficult to follow I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't like last commits. I think it is a task for Get-Help to return enhanced information in human format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@iSazonov Actually, it's a task for Get-Command, and that's exactly what it does.
Get-Command gci shows you that gci is an alias for Get-Command.
With this PR, Get-Command gci -Syntax shows you the syntax for the gci command and also identifies that it's an alias for Get-ChildItem.
There's no reason to force users to have to call Get-Command twice to get such important information.
In fact, it would probably be far more useful for a less experienced user if -Syntax reported this additional information, as if the user had $PSDefaultParameterValues['Get-Command:Syntax'] = $true in their profile, because unless they are programmatically working with command metadata or trying to find out what module a command comes from, this information is far more useful for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no reason to force users to have to call Get-Command twice to get such important information.
Get-Help is for users to get all in once. Get-Command is more for scripting - without -Syntax it returns typed object (AliasInfo or CommandInfo) and I'd even expect that -Syntax returns typed output too as a syntax tree.
|
In practice That said, this part of the discussion makes me wonder if |
I think maybe but thats a separate issue than this and whilst get-command already has syntax (and is in muscle memory) let's get improvements made to it |
|
I've added support for wildcards, with the correct alias for each command found replacing it's use in the syntax. If a command doesn't have an alias but is found with the wildcard then the extra notation isn't shown (see Split-Path example below) There is one odd case that I'm not sure what to do about, if an alias and the command it's for both match a wildcard then what should the output look like. An example is But I don't think it should, it should either produce the old output of just the |
|
I've discovered another issue this will have, if you do something like So I need to figure out a way to handle that but I know what's causing it so that's a start. |
|
I think I've fixed all the edge cases I can, the only one that's outstanding is if someone does This is now ready for a review since I refactored it into a new method. |
|
@SteveL-MSFT or @adityapatwardhan Can I get one of you to review this PR when you get a chance please? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NIT: Personally, would prefer the herestring to be assigned to a variable and use Set-Content on a different line as I think it's more readable
| '@ | out-File TestDrive:\Test-GcmSyntax.ps1 | |
| '@ | Out-File -Path TestDrive:\Test-GcmSyntax.ps1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| new-alias tgs TestDrive:\Test-GcmSyntax.ps1 | |
| new-alias tgs TestDrive:\Test-GcmSyntax.ps1 |
| new-alias tgs TestDrive:\Test-GcmSyntax.ps1 | |
| New-Alias -Name tgs -Value TestDrive:\Test-GcmSyntax.ps1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You repeat this line in each case, you can have it outside the switch statement
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are slightly different as the output will need to vary depending on what is found for the alias/wildcard. If the found command is a script then it's path needs to be added along with an extra new line as the syntax property of commands already includes a leading new line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm referring specifically to:
syntax = PSObject.AsPSObject(replacedSyntax);Unless my eyes are missing something isn't it exactly the same in each case statement before the break?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot, I hadn't noticed that. I've fixed that now to only happen once after the switch.
|
@iSazonov I think we all can agree that an object should be returned, but is outside the scope of this PR since returning a string is the existing behavior |
SteveL-MSFT
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
|
@PoshChan please retry windows |
|
@vexx32, successfully started retry of |
|
@ChrisLGardner I tried building your branch and am seeing some issues. This happens when a new pwsh.exe is started and the first command is PS> Get-Command del -Syntax
Get-Command: The term 'del' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Suggestion [4,General]: The most similar commands are: del, dir, dbp, fl, gal, gl, iex, nal, sal, sl.
PS> Get-Command del -Syntax
Get-Command: The term 'del' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Suggestion [4,General]: The most similar commands are: del, dir, dbp, fl, gal, gl, iex, nal, sal, sl.
PS> Get-Command del -Syntax
Get-Command: The term 'del' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Suggestion [4,General]: The most similar commands are: del, dir, dbp, fl, gal, gl, iex, nal, sal, sl.
PS> Get-Command del -Syntax
Get-Command: The term 'del' is not recognized as the name of a cmdlet, function, script file, or operable program.
Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
Suggestion [4,General]: The most similar commands are: del, dir, dbp, fl, gal, gl, iex, nal, sal, sl.
PS> Get-Command del
CommandType Name Version Source
----------- ---- ------- ------
Alias del -> Remove-Item
PS> Get-Command del -Syntax
del (alias) -> Remove-Item
del [-Path] <string[]> [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Force] [-Credential <pscredential>] [-WhatIf] [-Confirm] [-Stream <string[]>] [<CommonParameters>]
del -LiteralPath <string[]> [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Force] [-Credential <pscredential>] [-WhatIf] [-Confirm] [-Stream <string[]>] [<CommonParameters>] |
|
That's really weird, I'd not seen it before because it was auto loading my profile and that must be doing something to get around that. I'll have a look into it over the next day or two and see if I can figure out what's going on there. |
|
@adityapatwardhan It looks like an issue in the session state and is in Preview 5 too. It looks like not all aliases are having their ResolvedCommand and other properties populated properly until after at least one command is successfully ran first. If you do I'll log a separate issue about this as it's outside the scope of this PR and way out of my knowledge area. |
|
@ChrisLGardner I took a quick look at this, and the issue is module auto-loading. In a new session loaded without a profile, if I invoke If in that same session I invoke This makes me think it might be in scope for this PR. Whatever Note that I say all of this without looking at the code, so this is theoretical, but I think that this work is related enough to this PR and shouldn't be too difficult to figure out since |
Return aliased command syntax instead of the name of the aliased command
…n the helper method
|
@KirkMunro thanks for the pointer there, I managed to find the method in CommandDiscovery that was already being used for command resolution and added an extra check in to call that if it hasn't resolved the alias to a CommandInfo. @adityapatwardhan looks like I've resolved that bug if you get a chance to look at this again at some point. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems this will be dead code now? Is that correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisLGardner ping..
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, doing some testing and it's there to resolve the command name if the module the alias is in hasn't been loaded yet and then that can be used to get the syntax. Without it you get the below behaviour:
PS> gcm del -syn
Remove-Item
with it you get this:
PS> gcm del -syn
del (alias) -> Remove-Item
del [-Path] <string[]> [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Force] [-Credential <pscredential>]
del -LiteralPath <string[]> [-Filter <string>] [-Include <string[]>] [-Exclude <string[]>] [-Recurse] [-Force] [-Credential <pscredential>] [-WhatIf] [-Confirm] [-Stream <string[]>] [<CommonParameters>]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ChrisLGardner I think @adityapatwardhan is referring to the line 1312 if (current == null) code path, which shouldn't be reached anymore since you're checking for null and accounting for that directly. If whatever's in that code path isn't reachable anymore, it should be removed. 🙂
Also, this condition with the null can be simplified:
current = ai.ResolvedCommand ?? CommandDiscovery.LookupCommandInfo(
ai.UnresolvedCommandName,
this.MyInvocation.CommandOrigin,
this.Context);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I'll get that fixed now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at the failure in the tests there's a reason that check is there. It can sometimes return null from LookupCommandInfo. In the case for the tests (and locally) it is because spvm has a null ResolvedCommand and UnresolvedCommandName, probably in my case because I've apparently got both 1.1 and 2.0.0.0 of Hyper-V installed and I'd guess the AzDOS agents are similar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to add a comment explaining that so nobody tries to remove that later 🙂
…t if module is not imported
|
Looks like the last failing build is related to Test-Connection so not impact by the change I've made. though I'm certainly well behind master now but I can rebase if required. |
|
@PoshChan please retry macos |
|
@vexx32, successfully started retry of |
|
🎉 Handy links: |
PR Summary
Add an additional check into the
IsCommandMatchmethod to return the syntax of an aliased command rather than just the name of the aliased command.Previous behaviour:

New behaviour:

PR Context
#5963 asked for this functionality as currently
Get-Command del -syntaxdoesn't give you anything useful, especially not beyond whatGet-Command deldoes. The Issue does suggest that instead of the original command it shows the alias in the syntax but theSyntaxproperty is read-only and I didn't want to mess around with the base CommandInfo or AliasInfo object for something like this, not doing that also somewhat enables @KirkMunro's request that it shows thealias -> real commandas part of the output since it'll now show the resolved command name in the syntax'I don't think this is a breaking change because the likelihood of anyone actually using
Get-Command -Name <alias> -Syntaxfor anything outside the command line is very minimal. I'm also not sure if there needs to be a docs change for it but it might be useful just so users are aware that it'll do this now.PR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:or[ WIP ]to the beginning of the title (theWIPbot will keep its status check atPendingwhile the prefix is present) and remove the prefix when the PR is ready.