javaspring-bootparsingcommand-line-arguments

Parsing command line args that can contain spaces


I am building a spring boot based command line utility written in java for teams in my organization. I will have to allow teams to pass arbitrary command line args that can contain spaces.

I tried looking up any third party libraries or libraries from Spring that can do this but I am not able to find any that can can parse args the way I want it.

java -jar client.jar --args='a=Car b=Truck c="Car faster than Truck" d="Car faster than all"'

In my command line utility I would like to get the args.

a=Car
b=Truck
c=Car faster than Truck
d=Car faster than all

I tried using regex to split by spaces and as you can tell it wouldnt work. I think splitting it based on spaces wouldnt work too.

What would be the efficient way to parse these args?


Solution

  • Now I don't know anything about Spring-Boot but I'm going to take a chance here that the command line is similar to pretty much anything else. The overall goal here is to split the command-line string based on a whitespace but, not within double quote marks. You can do this with regex using a Positive Lookahead expression and the String#split() method. Below is a simple example (be sure to read the comments in code):

    /* Splits the supplied command-line string based on one (or more) 
       whitespace(s) but, not those that are contained within double 
       quote marks. This utilizes a Positive Lookahead regular expression
       for the String#split() method. If you want an explanation of 
       the regex used below, then copy/Paste: 
                \s{1,}(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)
       into the Regular Expression bar in https://regex101.com/      */
    String argsString = "'a=Car b=Truck c=\"Car faster than Truck\"  d=\"Car faster than all\"'";
        
    String regex = "\\s{1,}(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)";
        
    /* Not exactly sure if the single quotes at the beginnind and end 
       of of the overall command-line is actually delivered, so the `if` 
       block below removes them. If they are not part of the overall 
       command-line string then you can just delete this `if` block. */
    if (argsString.startsWith("'") && argsString.endsWith("'")) {
        argsString = argsString.substring(1, argsString.length() - 1);
    }
        
    // Create an array of the provided command-line arguments:
    String[] args = argsString.split(regex, -1);
        
    /* If you want, remove the double-quote marks from tag values 
       within command-line elements:        */
    for (int i = 0; i < args.length; i++) {
        /* Done this way in case there are nested double-quote marks
           (for whatever reason) within the command line Tag Values.
           nested double-quote marks are NOT parsed out: */
        String[] tmp = args[i].split("=");
        if (tmp[1].startsWith("\"") && tmp[1].endsWith("\"")) {
            args[i] = tmp[0] + "=" + tmp[1].substring(1, tmp[1].length() - 1);
        }
    }
        
    // Display the command-line elements within the console window:
    for (String str : args) {
        System.out.println(str);
    }
        
    System.out.println();
        
    /* Further parsing would be to split each arg element 
       based on the "=" character:         */
    System.out.println("Tag/Value Delimiter:   =");
    StringBuilder sb = new StringBuilder("");
    for (String arg : args) {
        String[] elements = arg.split("=");
        sb.append(String.format(
                  "Argument Tag: -> %-5s Argument Value: -> %-25s%n", 
                  elements[0], elements[1]
                ));
    }
    System.out.println(sb.toString());
    

    If this code is tasked then you should see something like what is displayed below within the console window:

    a=Car
    b=Truck
    c=Car faster than Truck
    d=Car faster than all
    
    Tag/Value Delimiter:   =
    Argument Tag: -> a     Argument Value: -> Car                      
    Argument Tag: -> b     Argument Value: -> Truck                    
    Argument Tag: -> c     Argument Value: -> Car faster than Truck    
    Argument Tag: -> d     Argument Value: -> Car faster than all