2

I have a string variable:

commandLineString := `echo -n "a b c d"`

I want to covert it to:

args := []string{"echo", "-n", "\"a b c d\""}

How can I do it?

icza
  • 389,944
  • 63
  • 907
  • 827
chengbin du
  • 512
  • 4
  • 13
  • https://www.google.co.id/#q=golang+split+string+by+space+keep+quoted+string – har07 Apr 21 '17 at 07:43
  • 1
    Specifically, [this thread](https://groups.google.com/forum/#!topic/golang-nuts/pNwqLyfl2co) which suggested several approaches one of them using library [go-shellwords](https://github.com/mattn/go-shellwords) – har07 Apr 21 '17 at 07:46
  • @har07 It works for what I needed, thank you so much! – chengbin du Apr 21 '17 at 12:42

2 Answers2

1

This can be expressed using regular expression in a very compact way.

The input (command) is a series of tokens that are:

  1. either non-quoted and cannot contain quotes and spaces,
  2. or quoted and spawn until the next quotation mark and can contain spaces (but not quotation mark).

And:

  1. Tokens are separated by spaces, or the end of input.

The regular expression from the listed criteria:

           ("[^"]*"|[^"\s]+)(\s+|$)

Criteria:   __2____ __1___   __3__

Using Go's regexp package the solution is quite short:

s := `echo -n "a b c d"`

pattern := `("[^"]*"|[^"\s]+)(\s+|$)`

r := regexp.MustCompile(pattern)

fmt.Printf("%q\n", r.FindAllStringSubmatch(s, -1))
fmt.Printf("%q\n", r.FindAllString(s, -1))

Output (try it on the Go Playground):

[["echo " "echo" " "] ["-n " "-n" " "] ["\"a b c d\"" "\"a b c d\"" ""]]
["echo " "-n " "\"a b c d\""]

Note that the result of regexp.FindAllString() also contains the delimeters (spaces) between tokens, so you may call strings.TrimSpace() on them to remove those:

ss := r.FindAllString(s, -1)
out1 := make([]string, len(ss))
for i, v := range ss {
    out1[i] = strings.TrimSpace(v)
}
fmt.Printf("%q\n", out1)

Which gives the desired output:

["echo" "-n" "\"a b c d\""]

Or you may use the result of regexp.FindAllStringSubmatch(): it returns a slice of slices, use the 2nd element (at index 1) from each element:

sss := r.FindAllStringSubmatch(s, -1)
out2 := make([]string, len(sss))
for i, v := range sss {
    out2[i] = v[1]
}
fmt.Printf("%q\n", out2)

Which also gives the desired output:

["echo" "-n" "\"a b c d\""]

Try these on the Go Playground).

icza
  • 389,944
  • 63
  • 907
  • 827
  • There are a couple of problems with this approach: first, it includes the quotes with quoted parameters, which os.Args does not, and which requires the code using it to deal with them; second, it doesn't account for escaped quotes in arguments. – Adrian Apr 21 '17 at 13:40
  • @Adrian It does not include quotes with quoted parameters, that's only how I print it with the `%q` verb to make sure they are there. Print it with `%v` and you'll see they are normal quotes. – icza Apr 21 '17 at 13:43
  • won't work for commands like curl where it will take even JSON data – Rahul Singh Raghav Apr 30 '20 at 05:27
-3

If the string are fix have 3 parameters you can do by strings.SplitN. here are the full documentation about strings library of golang. https://golang.org/pkg/strings/#SplitN

CMIIW ^^

  • How does this keep quoted strings? – md2perpe Apr 21 '17 at 07:56
  • @md2perpe using `SplitN()` to split by space specifying maximum split to 3 so the substring after the 2nd space won't be split anymore.. Only work with fix number of params and only the last param can contains space. Anyway, this work with OP's specific sample.. – har07 Apr 21 '17 at 08:07