Skip to content
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

Option values starting with dashes don't work #118

Open
mbunkus opened this issue Jul 14, 2021 · 3 comments
Open

Option values starting with dashes don't work #118

mbunkus opened this issue Jul 14, 2021 · 3 comments

Comments

@mbunkus
Copy link

mbunkus commented Jul 14, 2021

I have a program taking options with values. Sometimes those values start with a dash. If that happens, optimist tries to parse those as actual options instead of values, leading either to wrong options being activated or interested error messages.

The expected behavior is that the value after an option requiring a value is always assigned to the that option.

Here's an example:

#!/usr/bin/env ruby

require "optimist"
require "pp"

o = Optimist::options do
  opt :action, "an action", :type => :string
  opt :ignore, "ignore stuff"
end
pp o

Here are a couple of examples:

[0 mosu@sweet-chili ~/test] ./ruby1.rb --action jump
{:action=>"jump", :ignore=>false, :help=>false, :action_given=>true}
[0 mosu@sweet-chili ~/test] ./ruby1.rb --action --jump
Error: unknown argument '--jump'.
Try --help for help.
[255 mosu@sweet-chili ~/test] ./ruby1.rb --action -a --ignore
Error: option '-a' specified multiple times.
Try --help for help.
[255 mosu@sweet-chili ~/test] ./ruby1.rb --action --ignore
Error: option '--action' needs a parameter.
Try --help for help.

The first case is obviously OK.

The other cases are all really bad. In each case the value after --action (--jump in case 2, -a in case 3 and --ignore in case 4) should be assigned to the action option as the spec says that it must have a value.

Background: this is from a script doing DNS validation for the ACME protocol (Let's Encrypt certificates). It has to deal with the auth tokens that the Let's Encrypt servers generate. Today such an auth token started with a dash, and my program broke. I do not have any control over the auth tokens, which characters they consist of etc. A workaround such as "use different argument formats, then" doesn't help if I don't have control over the data my callers must use.

@Fryguy
Copy link
Member

Fryguy commented Jul 14, 2021

Does the following work for you?

./ruby1.rb --action="--jump"

@mbunkus
Copy link
Author

mbunkus commented Jul 14, 2021

Yes, it works, and yes, I can use that as a workaround, thanks.

I still consider this to be a bug, though. If the CLI argument in position N is an option requiring a value and doesn't contain a =, there's no way the argument in position N+1 should be interpreted as anything else but the value to that option. Anything else is really surprising to users, especially if the interpretation depends on its contents.

For me the situation seems slightly different when the option has a default. Let's take this small modification of my original program (only adding a default to :action):

#!/usr/bin/env ruby

require "optimist"
require "pp"

o = Optimist::options do
  opt :action, "an action", :type => :string, :default => "default_action"
  opt :ignore, "ignore stuff"
end
pp o

Let's run this:

[0 mosu@sweet-chili ~/test] ./ruby1.rb --action --ignore
{:action=>"default_action",
 :ignore=>true,
 :help=>false,
 :action_given=>true,
 :ignore_given=>true}

I can see how that is ambiguous and no fun situation to be in.

I haven't done an extensive study of existing command line parsers out there, and I'm well aware that there's no standard for this. Just a couple of examples:

  • sed -e -E interprets -E, which is generally a valid option, as the expression to evaluate (the value to -e which requires a value) and consequently omits a syntax error, which is expected.
  • Same with tcpdump -i -n port 443 which interprets -n, a valid option, as the argument to -i which requires an option. Same with the variant tcpdump --interface -n port 443 and tcpdump --interface=-n port 443 ; all three invocations are parsed identically.
  • Again, same with less -P -i ruby1.rb, here -i becomes the prompt (which is what -P sets) even though -i is a valid option itself.

@Fryguy
Copy link
Member

Fryguy commented Jul 14, 2021

Yeah, I can see both sides to the issue due to the ambiguities. I think it would be most helpful to see how other option parsers handle things like this.

I think it also gets tricky with the "strings" type, where we wouldn't know where the next option would start. I'm not sure how we could resolve that in a consistent way with other options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants