-
Notifications
You must be signed in to change notification settings - Fork 424
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
Add support for passing through parameters to another command (was: How to handle find -exec
kind-of open-ended options?)
#718
Comments
You should be able to use Running the example exposed a bug (thank you!) where the default end-of-options Can you take this example for a spin (ideally after building from master) and let me know if this meets your needs? |
Sure thing. I'll report back on Monday! |
I created #719 for the end-of-options bug. |
Is there an API that would allow me to specify my own do my own argument parsing, akin to e.g.
e.g. akin to this API here: |
The idea behind Somehow I don't think that this is really what you want to do though. Can you explain your use case in more detail? What is the problem you are trying to solve? I can see that the
|
|
Here's an example for my command-line tool usage: e.g.
|
Filebot is an impressive project! Usually the end-of-options delimiter is consumed by the parser and the remainder is treated as positional parameters. If you need to distinguish between Happy to help try different approaches until we find something that meets your needs. I’ll try to experiment more with the examples you gave me so far. Do you have a branch with work in progress that I can look at? If you have more examples of how users may invoke your tool and how you would like to process that input, that would be useful. |
So I've been using |
CLI options are a bit messy right now though, so I am thinking about a rewrite and reorganize with |
Ok, interesting. The difference between arg4j's For example, your command has a
In the above example, the only argument passed to the Passing through some of the arguments to a 3rd party command is an interesting use case and you are not the first to raise it. arg4j's Other than this, are there any other missing features or behaviour that differs from args4j that prevent you from migrating to picocli? |
I think that was the only one that stuck out. I think the general use case here is some sort of interface that will allow one to use code to dynamically define arity based on the command-line values given, rather than statically. e.g. I do have another custom handler though:
So I can do:
As a less verbose way of doing:
I know that the verbose way is supported by picocli, but I'm not sure the short form. Maybe it's implicitly supported but I don't remember seeing anything in the examples. This one will just read args until the next |
You won't need a custom handler for map values in picocli. It provides what you need out of the box, and in addition you can have strongly typed keys and/or values. To illustrate: @Test
public void testMapOptionArity1_n() {
class MapParamsArity1_n {
@Option(names = {"-D", "--def"}, arity = "1..*", split = ",")
Map<String, Integer> params;
}
// most verbose
MapParamsArity1_n params = CommandLine.populateCommand(
new MapParamsArity1_n(), "--def", "a=1", "--def", "b=2", "--def", "c=3");
Map<String, Integer> expected = new LinkedHashMap<String, Integer>();
expected.put("a", 1);
expected.put("b", 2);
expected.put("c", 3);
assertEquals(expected, params.params);
// option name once, followed by values
params = CommandLine.populateCommand(
new MapParamsArity1_n(), "--def", "aa=11", "bb=22", "cc=33");
expected.clear();
expected.put("aa", 11);
expected.put("bb", 22);
expected.put("cc", 33);
assertEquals(expected, params.params);
// most compact (using the split regex)
params = CommandLine.populateCommand(
new MapParamsArity1_n(), "-Dx=4,y=5,z=6");
expected.clear();
expected.put("x", 4);
expected.put("y", 5);
expected.put("z", 6);
assertEquals(expected, params.params);
try {
params = CommandLine.populateCommand(new MapParamsArity1_n(), "--def");
fail("Should not accept input with missing parameter");
} catch (MissingParameterException ex) {
assertEquals("Missing required parameter for option '--def' at index 0 (<String=Integer>)",
ex.getMessage());
}
} |
My bad. Looks like it works as expected right out of the box. :) |
Great, thanks for the confirmation. About the custom argument handler, I'm thinking to introduce an interface like this: /**
* Options or positional parameters can be assigned a {@code IParameterConsumer} that implements
* custom logic to process the parameters for this option or this position.
* When an option or positional parameters with a custom {@code IParameterConsumer} is matched on the
* command line, picocli's internal parser is temporarily suspended, and this object becomes
* responsible for consuming and processing as many processing command line arguments as needed.
* <p>This may be useful when passing through parameters to another command.</p>
* <p>Example usage:</p>
* <pre>
* @Command(name = "find")
* class Find {
* @Option(names = "-exec", parameterConsumer = Find.ExecParameterConsumer.class)
* List<String> list = new ArrayList<String>();
*
* static class ExecParameterConsumer implements IParameterConsumer {
* public void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec) {
* List<String> list = argSpec.getValue();
* while (!args.isEmpty()) {
* String arg = args.pop();
* list.add(arg);
*
* // `find -exec` semantics: stop processing after a ';' or ' ' argument
* if (";".equals(arg) || " ".equals(arg)) {
* break;
* }
* }
* }
* }
* }</pre>
* @see Option#parameterConsumer()
* @see Parameters#parameterConsumer()
* @since 4.0 */
public interface IParameterConsumer {
/**
* Consumes as many of the specified command line arguments as needed by popping them off
* the specified Stack. Implementors are free to ignore the {@linkplain ArgSpec#arity() arity}
* of the option or positional parameter, they are free to consume arguments that would
* normally be matched as other options of the command, and they are free to consume
* arguments that would normally be matched as an end-of-options delimiter.
* <p>Implementors are responsible for saving the consumed values;
* if the user object of the option or positional parameter is a Collection
* or a Map, a common approach would be to obtain the current instance via the
* {@link ArgSpec#getValue()}, and add to this instance. If the user object is an
* array, the implementation would need to create a new array that contains the
* old values as well as the newly consumed values, and store this array in the
* user object via the {@link ArgSpec#setValue(Object)}.
* </p><p>
* If the user input is invalid, implementations should throw a {@link ParameterException}
* with a message to display to the user.
* </p><p>
* When this method returns, the picocli parser will process the remaining arguments on the Stack.
* </p>
* @param args the command line arguments
* @param argSpec the option or positional parameter for which to consume command line arguments
* @param commandSpec the command that the option or positional parameter belongs to
* @throws ParameterException if the user input is invalid
*/
void consumeParameters(Stack<String> args, ArgSpec argSpec, CommandSpec commandSpec);
} One issue that needs to be addressed is that the registry of type converters is currently private. This registry needs to be exposed as API, to allow |
That was fast! Looks good to me. That should give very high level of flexibility for the people who need it. I'd just |
Let me know if you have a revision where this is already included. Then I can try to build a sample for my |
Ok. I’ll try to push something to master in a few hours. |
…Xxx.class)` attribute for passing arguments through to another command, like `find -exec`
This is now in master. If you want to try this, it takes about 1 minute to build the project:
That will create the jar in |
find -exec
kind-of open-ended options?find -exec
kind-of open-ended options?)
Update: I renamed the interface to |
Closing this ticket, I think there is no more work left. |
Sure thing. I'll do some testing when time allows this week. |
Here's the solution I came up with: |
Nice! That looks pretty clean. 🎉 Let me know if you need anything else to migrate filebot to picocli. FYI picocli 4.0.0-beta-2 has been released with this feature. |
Nice, the new docs are simple and to the point. |
Is there a way to parse open-ended parameters that may contain
-
or--
up until some predefined end-of-parameter-sequence marker or the end of the argument array?e.g. If I wanted to do something like
find -exec
, how would I go about it?ls -la {}
is an arbitrary command-template called for a given set of files by my application in a child process.The text was updated successfully, but these errors were encountered: