2

I'm faced with a need for parsing a string into key-value pairs, where the value may be optional. Standard command line parsers are not useful, because all the ones I checked accept a String[] and not a String. Thus, I resorted to regex, and sure enough, faced with the following:

Some people, when confronted with a problem, think "I know, I'll use regular expressions." Now they have two problems.

First, the input string:

"/opt/sensu/embedded/bin/ruby /opt/sensu/embedded/bin/check-graphite-stats.rb " +
"--crit 25 --host 99.99.999.9999:8082 --period -5mins --target 'alias(scale(divideSeries(" +
"summarize(sumSeries(nonNegativeDerivative(transformNull(exclude(" +
"\\\"unknown\\\"), 0))), \\\"30d\\\", \\\"sum\\\", false),summarize(" +
...gigantuous string
\\\"sum\\\", false)), 100), \\\"3pp error rate\\\")' " +
"--unknown-ignore --warn 5"

Next, my regex:

(--(?<option>.+?)\s+(?<value>.+?(?=--))?)+?

the above almost works, but not quite.

Output:

--crit 25 
--host 99.99.999.9999:8082 
--period -5mins 
--target 'gigantuous string' 
--unknown-ignore 
--warn 

Why is the value of --warn not picked up?

4
  • Why not just pass each parameter separately into args[]? Commented Nov 8, 2018 at 1:46
  • Your input is a shell command. How did you end up trying to write a shell parser in Java? Commented Nov 8, 2018 at 1:48
  • All I've is the string. Commented Nov 8, 2018 at 1:59
  • @thatotherguy, I can't speak for the poster, but I want to be sure the web API I'm unit testing is invoking a shell command properly. Commented Jan 26, 2023 at 18:31

1 Answer 1

1

Because you're doing a positive lookahead to the next -- at the end of the regex ((?=--)), the value of the last parameter in the string isn't picked up as it's not followed by --. Accepting the end of the string as an alternative ((?:(?=--)|$)) and then filtering values that don't start with -- (by replacing .+? with .(?:[^-].+?)?) should behave in the way you want:

(--(?<option>.+?)\s+(?<value>.(?:[^-].+?)?(?:(?=--)|$))?)+?

(However, as others have mentioned, I'd be very surprised that there isn't a Java argument parsing library that would suit your use case. Even if it means writing the code to split your string into arguments yourself, it might be less brittle.)

Sign up to request clarification or add additional context in comments.

5 Comments

Nop! it picks up --unknown-ignore --warn 5 together.
Oh yes, so it does. What about adding [^-] at the start of the value group? ((--(?<option>.+?)\s+(?<value>[^-].+?((?=--)|$))?)+?)
No. I appreciate your time, but there are plenty of regex testers online, you should check first; this seems like throwing Spaghetti at the wall hoping it would stick.
Ah, sorry, looks like I misconfigured my tester. (--(?<option>.+?)\s+(?<value>.([^-].+?)?((?=--)|$))?)+? should work.
Some of the groups were not necessary, I converted them to non-capturing (--(?<option>.+?)\s+(?<value>.(?:[^-].+?)?(?:(?=--)|$))?)+?. Other than that, your answer works for me, so I accepted it. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.