I'm looking for a package that would take a string such as -v --format "some example" -i test
and parse it into a slice of strings, handling quotes, spaces, etc. properly:
-v
--format
some example
-i
test
I've checked the built-in flag
package as well as other flag handling packages on Github but none of them seem to handle this particular case of parsing a raw string into tokens. Before trying to do it myself I'd rather look for a package as I'm sure there are a lot of special cases to handle.
Any suggestion?
For information, this is the function I've ended up creating.
It splits a command into its arguments. For example, cat -v "some file.txt"
, will return ["cat", "-v", "some file.txt"]
.
It also correctly handles escaped characters, spaces in particular. So cat -v some\ file.txt
will also correctly be split into ["cat", "-v", "some file.txt"]
func parseCommandLine(command string) ([]string, error) {
var args []string
state := "start"
current := ""
quote := "\""
escapeNext := true
for i := 0; i < len(command); i++ {
c := command[i]
if state == "quotes" {
if string(c) != quote {
current += string(c)
} else {
args = append(args, current)
current = ""
state = "start"
}
continue
}
if (escapeNext) {
current += string(c)
escapeNext = false
continue
}
if (c == '\\') {
escapeNext = true
continue
}
if c == '"' || c == '\'' {
state = "quotes"
quote = string(c)
continue
}
if state == "arg" {
if c == ' ' || c == '\t' {
args = append(args, current)
current = ""
state = "start"
} else {
current += string(c)
}
continue
}
if c != ' ' && c != '\t' {
state = "arg"
current += string(c)
}
}
if state == "quotes" {
return []string{}, errors.New(fmt.Sprintf("Unclosed quote in command line: %s", command))
}
if current != "" {
args = append(args, current)
}
return args, nil
}