10

I'm writing a shell in Java because I haven't programmed in Java for far too long and I've forgotten a lot of it. I was going to write my own, but right now I'd like to get a working prototype, so I would like to drop in an existing command-line option parser.

Requirements:

  • Takes an arbitrary String or String[] (i.e. doesn't use the arguments from the command line)
  • Takes arguments in a similar format to getopt (the GNU enhanced version); specifically:
    • Multiple single-letter options can be joined (e.g. -a -b -c to -abc)
    • Supports long options (e.g. --message="Hello!") (though it doesn't have to support single-dash long options)
    • Assumes all non-option-like bits at the end are parameters to be passed normally (e.g. -abc --long="Hello!" param1 param2 tells me that the parameters are param1 and param2)
    • -- can be used to separate options from arguments (e.g. -ab --custom="hello" -- -file_starting_with_hyphen -another gives me the options/flags a, b, and custom with the appropriate values, and tells me that the arguments are -file_starting_with_hyphen and -another)
    • Whitespace can be part of arguments, if it's quoted.
    • An option name followed by a value is parsed as the option having that value (e.g. -h foo says that there is an option, h, with a value, foo)
  • Entirely cross-platform
  • Doesn't need me to specify which options I'm looking for (i.e. I pass it a String or String[] and it tells me which flags/options were set, as opposed to it looking for the options I want to set and assuming the rest are arguments)
    • This means it doesn't use annotations to specify where to store the flag values. At least two answers so far have done this.
  • Free (as in beer)
  • Can be legally used in any project (i.e. not noncommercial, not GPL)

Ideal, but not necessary:

  • Small -- one file
  • Minimal copyright license (Not copyleft -- I like not worrying about legal issues, and I hate people trying to tell me that I can't use my work, however, I damned well, please)
  • Uses the built-in interfaces (java.util.Map, specifically) to return data. (This is so I can write my own function more easily later)
  • Open source
  • @a_horse If I recall correctly, I didn't use that one because it requires me to specify which options I'm looking for. See the requirements in the question. – Nic Hartley Oct 12 '16 at 18:52
  • I'm pretty sure getopt also requires you to specify the arguments you're looking for. And it definitely allows single-char arguments to be joined together, so how do you differentiate between "-top" as a long argument and the combination of -t, -o, and -p without specifying what's permissible? I mention this because "works like getopt" is your second requirement. – CPerkins Jan 15 '17 at 18:23
  • @CPerkins "like" means "similarly to", here, not "in precisely the same way as". – Nic Hartley Jan 15 '17 at 21:33
  • Okay, thanks. New question: it seems to me that the combination of the "non-option-like bits at the end are parameters" and "whitespace is allowed in arguments" is ambiguous. What if the arguments end with "-n=alpha beta"? Is "beta" part of the value of n? Or is it a parameter? – CPerkins Jan 17 '17 at 16:18
2

You could check out FeSimpleArgs

I believe it fulfills all your requirements:

Concept: A very light-weight command-line parser. Takes arguments in either a String or String[] form, returns a class instance holding the arguments in a map, and the parameters in a list.

Requirements:

  • Takes an arbitrary String or String[] (i.e. doesn't use the arguments from the command line) Yes
  • Takes arguments in a similar format to getopt (the GNU enhanced version); specifically:
  • Multiple single-letter options can be joined (e.g. -a -b -c to -abc) Yes
  • Supports long options (e.g. --message="Hello!") (though it doesn't have to support single-dash long options) Yes
  • Assumes all non-option-like bits at the end are parameters to be passed normally (e.g. -abc --long="Hello!" param1 param2 tells me that the parameters are param1 and param2) Yes
  • -- can be used to separate options from arguments (e.g. -ab --custom="hello" -- -file_starting_with_hyphen -another gives me the options/flags a, b, and custom with the appropriate values, and tells me that the arguments are -file_starting_with_hyphen and -another) Yes
  • Whitespace can be part of arguments, if it's quoted. Yes
  • An option name followed by a value is parsed as the option having that value (e.g. -h foo says that there is an option, h, with a value, foo) Yes

  • Entirely cross-platform Yes

  • Doesn't need me to specify which options I'm looking for (i.e. I pass it a String or String[] and it tells me which flags/options were set, as opposed to it looking for the options I want to set and assuming the rest are arguments) Yes

as to usage:

  • Free (as in beer) Yes - Apache 2.0
  • Can be legally used in any project (i.e. not noncommercial, not GPL) Yes - Apache 2.0
  • Minimal copyright license (Not copyleft -- I like not worrying about legal issues, and I hate people trying to tell me that I can't use my work, however, I damned well, please) Yes - Apache 2.0

Published under Apache 2.0 license.

Ideal, but not necessary:

  • Small -- one file Yes (one file for the parser, one for the unit tests, not needed to parse)
  • Uses the built-in interfaces (java.util.Map, specifically) to return data. (This is so I can write my own function more easily later) **Sort of ** (Returns a class holding a java.util.Map of arguments and a List of parameters)

USAGE Usage: 1) Construct an instance of FeSimpleArgs

FeSimpleArgs parser = new FeSimpleArgs();

2) Use it to parse your arguments:

FeSimpleArgs.Result result = parser.parse ("-def=value1 --GHI=value2 -a -b -c=value3 -n=\"foo bar baz\" -- param1 -param2"); 

3) Examine your results: From the above, result will consist of a Map containing the following arguments and flags (note that there's no real meaning to "argument" or "flag", I'm just using those terms to separate whether they take values or not - all are in the same Map).

  • a
  • b
  • d
  • e
  • f with value=value1
  • GHI with value=value2
  • c with value=value3
  • n with value=foo bar baz (note quotes are stripped)

And a list containing these parameters (note that the leading hyphen is preserved) - param1 - -param2

4) Profit?

For other examples, please see the included unit tests.

Disclaimer: I am the author of FeSimpleArgs

  • This seems perfect, thanks! One thing, though; just glancing through the code, I see a few things that're off -- repeatedly compile-ing a Pattern that never changes, tokenize2 never being used, and so on. You might want to post a question on CodeReview.SE. – Nic Hartley Jan 20 '17 at 16:15
  • Good observations. Tokenize2 was intended to be an attempt using a different regex. The one now doesn't do what I'd really like, which is to pull the entire quoted string even when there's stuff before it. And I don't like having to strip the quotes. It's still arguably a work in progress, but even in its imperfect state, it passes all the tests. – CPerkins Jan 20 '17 at 18:46
0

I have used args4j successfully in a number of projects. It was developed by Kohsuke Kawaguchi who who also developed Jenkins, was main developer for JAXB and other projects, so he has a very good pedigree.

  • I think I phrased it badly in the question, but I'm looking for something that I can pass a String[] to and get back (for example) a Map of options, not something that automatically parses and assigns the options from the arguments given to the jarfile. Also, it has to be able to tell me what options have been passed with what arguments, as opposed to me telling it what options to look for. – Nic Hartley Dec 28 '15 at 20:20
0

Note: This answer is outdated, because OP edited the question to add a requirement that this doesn't fulfill (however, it is still a darn good thing to know). See the comments below. The OP does NOT want you to downvote this answer.

The classic for this in Unix/Linux, back from the days of C was GetOpt().

Gnome has a Java port here.

  • I was looking for something less state-based (I think that's the word? I.e one function that returns a Map, instead of scanning through). Also, a requirement that I forgot to put in is that it has to be able to pull any arbitrary options from the string, not just ones I specify, since this is going to be passing in arguments for every command, and parsing the arguments for each would take far too much time. Still, I like it, if only because I can rifle through their code to see how they did it. – Nic Hartley Dec 2 '15 at 12:06
  • You had best update your question for that requirement that you forgot, so that people can help you. happy rifling! I hope this helped a little :-) – Mawg Dec 2 '15 at 12:37
  • I just did :D and like I said, it did help. Thanks! – Nic Hartley Dec 2 '15 at 12:54
  • And now that my answer no longer applies I am getting downvoted by ppl who can't be arsed to read the full thing. C'est la vie :-( – Mawg Dec 6 '15 at 19:39
  • Oh, dang. Sorry about that. It might be worth editing in that this was written before a couple of the requirements were added. – Nic Hartley Dec 6 '15 at 19:40
0

picocli may be of interest. It is a single source file to encourage command line app authors to include it as source as a simpler alternative to shading jars into an uberjar. It also fulfills all other requirements stated in the OP - UPDATE: except for the requirement that options cannot be fixed at compile time :-(.

UPDATE 2: picocli 3.x offers a programmatic API in addition to the annotations API, so now it meets all requirements. Note that Groovy CliBuilder is based on picocli since Groovy 2.5. This is exactly what the OP is asking for: dynamically add options and positional parameters to a command.

It recently added autocompletion functionality.

Another feature you may like is its colorized usage help.

Minimal usage help with ANSI colors

Parser features:

  • Annotation API: parsing is one line of code in your application
  • Programmatic API: everything that is possible with annotations can be done programmatically
  • Strongly typed everything - command line options as well as positional parameters
  • POSIX clustered short options (<command> -xvfInputFile as well as <command> -x -v -f InputFile) and GNU long options
  • An arity model that allows a minimum, maximum and variable number of parameters, e.g, "1..*", "3..5"
  • Subcommands (can be nested to arbitrary depth)
  • Works with Java 5 and higher

The usage help message is easy to customize with annotations (without programming). For example:

Extended usage help message (source)

I couldn't resist adding one more screenshot to show what usage help messages are possible. Usage help is the face of your application, so be creative and have fun!

picocli demo

Disclaimer: I created picocli. Feedback or questions very welcome.

  • I'm not sure you read my question completely. Please make sure you read through all the requirements. – Nic Hartley Aug 10 '17 at 12:02
  • Thank you for clarifying the question. I must have missed that in my enthusiasm. – Remko Popma Aug 10 '17 at 12:06
  • No worries. It seems like a great option for a regular command-line parser, but if that's what I wanted, I doubt I'd have had to ask a question; there are plenty of options out there. – Nic Hartley Aug 10 '17 at 12:08
  • Understood. Thanks for the positive feedback. I've updated my answer to highlight that it is not a good match. Hoping it is still a useful answer for other readers... – Remko Popma Aug 10 '17 at 12:16
  • 1
    Note that since picocli 3.x, picocli offers a programmatic API in addition to the annotations API. This meets all requirements of the OP. A notable example is Groovy CliBuilder: since Groovy 2.5, CliBuilder is implemented with the picocli programmatic API. – Remko Popma Feb 28 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.