Powershell: Feature Request: Shouldn't need to specify obvious parameter names when executing a cmdlet

Created on 28 Feb 2019  路  11Comments  路  Source: PowerShell/PowerShell

Suggestion
This should be possible:
"C:\Path\" | Join-Path "/ChildPath/";

When there is no ambiguity which parameter the unnamed parameter to a cmdlet should be mapped to, then it should not be required. In the example above, the command has two mandatory parameters, one comes va the pipeline, so only one left.

Implementation?
Could perhaps be achieved as such...
When a parameter is piped to a cmdlet, the "remaining" parameters should have their position decreased by one (params.Where(p=>p.Position>pipeParam.Position).ForEach(p=>p.Position--);)

Issue-Enhancement

Most helpful comment

The conceptual model with passing data between command in a pipe is something like:

"Here you have some data for you to process".

And if it is so that some other parameter binds to the data to be processed and making it impossible for the pipe to work, it's almost a bug.

All 11 comments

This should already work, honestly. This is easily permitted through the default implementations of parameter binding. Let me check out the parameter definitions, it's probably as simple as just adding positional binding to -ChildPath or something.

OKay, so the parameters all have positional bindings... I'd say the most likely explanation is that the binder isn't sure what you're trying to do when both piping things in and supplying a first positional param. I could fix it with parameter sets, I think.

Hmm. nope. Can't seem to get it to pick up what's being done there. @lzybkr do you have any insight as to why the parameter binding fails here?

Note: I'm speculating, so do tell me if I got things wrong:

  • Before _pipeline_ input is processed - possibly without even being considered in the abstract at that point - all parameters for which _arguments_ were passed are bound.

  • Therefore, "/ChildPath/" binds to -Path as the _first_ positional parameter.

  • Then pipeline input processing starts, and the only possible way to bind the string input object is to -Path as well (because it's the only parameter that binds from the pipeline _by value_).

  • Since -Path is already bound, you get a - somewhat misleading - error message:

The input object cannot be bound to any parameters for the command 
either because the command does not take pipeline input or the input 
and its properties do not match any of the parameters that take pipeline input.
  • The actual cause is neither; it is that there are no _still-unbound_ parameters that the pipeline input can bind to; in other words: the target parameter is already bound.

In the simplest case we could improve the error message.

Based on the above, there is, after all, an ambiguity, and reporting an error is one way to resolve it. (It's not an ambiguity; even though it may be surprising, it's predictable behavior.)

However, I think we agree that @Bartolomeus-649's suggestion to give precedence to pipeline input when binding by-value pipeline-binding parameters makes for a better user experience.

Specifically, the following approach may work:

  • At the time of _argument_ binding, look at whether there is pending pipeline input in the abstract.
  • If there is, take any ValueFromPipeline parameter out of the running for _positional_ binding.

Note that this won't work for ValueFromPipelineByPropertyName parameters, because knowing whether they would bind would require inspecting all input up front.

What if I want reverse logic? :-)
"/ChildPath/" | Join-Path "C:\Path"

It seem a bit strange that a command don't know if it is executing as part of a pipeline, or if it executing as the first command in a pipeline.
If the command is part of a pipeline execution (that is, not the first) then shouldn't the corresponding parameter be removed from further possible bindings?

@iSazonov Can't be done as -ChildPath only accepts pipeline input ByPropertyName. You would have to pass an object with a ChildPath property.

There is no ambiguity here, but I get the sense from the errors it gives that @mklement0's expectation is accurate. There needs to be logic somewhere in parameter binding to ensure that if a cmdlet is expecting pipeline input that the by-value pipeline parameters are not bound to by explicitly input parameters, excepting the scriptblock-input syntax (e.g., GCI | Join-Path -Path {$_.Directory} -ChildPath {$_.Extension})

The conceptual model with passing data between command in a pipe is something like:

"Here you have some data for you to process".

And if it is so that some other parameter binds to the data to be processed and making it impossible for the pipe to work, it's almost a bug.

Can't be done as

Why? I want! :-) It is my use case. Note that it is more "native" than use case from initial post.

It can't be done because only -Path is ValueFromPipeline -- -ChildPath is ValueFromPipelineByPropertyName.

Conceivably you could define two separate parameter sets, one that takes input with -ChildPath from pipeline, and one that takes only -Path from pipeline (both ByValue), but then _in both cases_ you would need to specify which of the parameters was being passed as a direct argument.

So it would still block this request, unless there can be agreement on which of those two behaviours ought to be "default". 馃槃

"\Child\Folders" | Join-Path -Path "C:\" # OK, unambiguous
"C:\" | Join-Path -ChildPath "\Child\Folders" # OK, unambiguous
"C:\" | Join-Path "\Child\Folders" # Ambiguous as to which is which; which should be default?

In either case, the parameter sets would need to be adjusted, and the underlying bug here would still have to be fixed.

@vexx32 I say not about implementation details but about user expectations.
"C:\" | Join-Path "\Child\Folders" vs "\Child\Folders" | Join-Path "C:\"
Here "default" is ambiguous too.
And we can fix only "\Child\Folders" | Join-Path -Path "C:\" as you suggest.

Was this page helpful?
0 / 5 - 0 ratings