alecthomas / kong

Kong is a command-line parser for Go
MIT License
1.98k stars 136 forks source link

Single/Double-quotes ignored in string or []string fields; impossible to have spaces in field values #433

Closed corto25 closed 2 days ago

corto25 commented 1 week ago

With the following struct:

type BuildCmd struct {
    Root      string   `help:"Relative path to the root directory of the project to build" required:""`
    Cmds      []string `help:"Commands to run for building" required:"" sep:";"`
}

And passing the following program arguments:

$> program build --root ~/projects/foo --cmds="make -j8 build/bar;mv -v {build,/tmp}/bar"

Or this variant program arguments (both produce the same unexpected results):

$> program build --root ~/projects/foo --cmds "make -j8 build/bar" --cmds "mv -v build/bar /tmp/bar"

Expectation is to get the equivalent of the following go assignments:

b := BuildCmd{
  Root: "~/projects/foo",
  Cmds: []string{
    "make -j8 build/bar",
    "mv -v build/bar /tmp/bar", // or "mv -v {build,/tmp}/bar" for the first example
  },
}

The actual behavior is a parse error:

error: unknown flag -j, did you mean "-h"?

I can't find a workaround: tried --cmds "make\ -j8\ build", tried --cmds "make\ \-j8\ build", tried --cmds 'make -j8 build', etc...

Affected versions: kong v0.8.0 and v0.9.0, go1.22.3

Root cause?

I've noticed the os.Args may be at fault and this is likely not a kong issue. Try printing log.Printf("Args: %#v", os.Args):

Args: []string{"./build/missionctl.x86_64", "build", "--root", "/home/simon/projects/foo", "--cmds=make", "-j8", "build/bar;mv", "-v", "{build,/tmp}/bar"} 

Since kong is adversely affected by this, so perhaps we can work together to find a suitable workaround.

My expectation is to get the same as this C++ program achieves:

#include <iostream>

int main(int argc, char* argv[]) {
  std::cout << "Agruments: " << argc << std::endl;

  for(int i = 0; i < argc; i++) {
    std::cout << "  Arg[" << i << "]: \"" << argv[i] << "\"" <<std::endl;
  }
  std::cout << "--eol" << std::endl;

  return 0;
}

Build with:

g++ -o test++ ./cmd/test.cc

Execute with the most awkward, yet most likely usage of the kong CLI:

$> ./test++ build --root ~/projects/foo --cmds="make -j8 build/bar;mv -v {build,/tmp}/bar"

Yields the following:

Agruments: 5
  Arg[0]: "./test++"
  Arg[1]: "build"
  Arg[2]: "--root"
  Arg[3]: "/home/user/projects/foo"
  Arg[4]: "--cmds=make -j8 build/bar;mv -v {build,/tmp}/bar"
--eol

Note the Arg[3] was expanded by the shell and Arg[4] was modified to remove the double-quotes within yet retain the whole thing as a single argument. My shell is bash.

alecthomas commented 1 week ago

I can't replicate this, it works exactly as it should.

alecthomas commented 1 week ago

Ah sorry, I didn't see the extra \" quotes you were mentioning, my bad.

alecthomas commented 1 week ago

Definitely a bug in Kong. I think what's happening is that it isn't unquoting the internal JSON value correctly before splitting.

alecthomas commented 2 days ago

Actually, nope sorry, I can't replicate it. I actually had an incorrect test case.

https://go.dev/play/p/2DzOikBF5o8 <- this works exactly as expected.

alecthomas commented 2 days ago

If you can provide a minimal runnable reproduction case, I'll take a look.

alecthomas commented 2 days ago

I've noticed the os.Args may be at fault and this is likely not a kong issue. Try printing log.Printf("Args: %#v", os.Args):

Args: []string{"./build/missionctl.x86_64", "build", "--root", "/home/simon/projects/foo", "--cmds=make", "-j8", "build/bar;mv", "-v", "{build,/tmp}/bar"} 

If this is the case, it definitely isn't a problem with Kong - there's something weird going on with your shell.