mirror of https://github.com/docker/cli.git
Replace mattn/go-shellwords with google/shlex
Both libaries provide similar functionality. We're currently using Google Shlex in more places, so prefering that one for now, but we could decide to switch to mattn/go-shellwords in future if that library is considered better (it looks to be more actively maintained, but that may be related to it providing "more features"). Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
parent
bece8cc41c
commit
251861237a
|
@ -17,7 +17,7 @@ import (
|
||||||
"github.com/docker/docker/api/types/versions"
|
"github.com/docker/docker/api/types/versions"
|
||||||
"github.com/docker/go-connections/nat"
|
"github.com/docker/go-connections/nat"
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
shellwords "github.com/mattn/go-shellwords"
|
"github.com/google/shlex"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
@ -853,7 +853,7 @@ func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool
|
||||||
|
|
||||||
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
|
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
|
||||||
if str, ok := value.(string); ok {
|
if str, ok := value.(string); ok {
|
||||||
return shellwords.Parse(str)
|
return shlex.Split(str)
|
||||||
}
|
}
|
||||||
return value, nil
|
return value, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,6 @@ github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce8
|
||||||
github.com/jaguilar/vt100 ad4c4a5743050fb7f88ce968dca9422f72a0e3f2 git://github.com/tonistiigi/vt100.git
|
github.com/jaguilar/vt100 ad4c4a5743050fb7f88ce968dca9422f72a0e3f2 git://github.com/tonistiigi/vt100.git
|
||||||
github.com/json-iterator/go 0ff49de124c6f76f8494e194af75bde0f1a49a29 # 1.1.6
|
github.com/json-iterator/go 0ff49de124c6f76f8494e194af75bde0f1a49a29 # 1.1.6
|
||||||
github.com/konsorten/go-windows-terminal-sequences edb144dfd453055e1e49a3d8b410a660b5a87613 # v1.0.3
|
github.com/konsorten/go-windows-terminal-sequences edb144dfd453055e1e49a3d8b410a660b5a87613 # v1.0.3
|
||||||
github.com/mattn/go-shellwords 36a9b3c57cb5caa559ff63fb7e9b585f1c00df75 # v1.0.6
|
|
||||||
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c # v1.0.1
|
github.com/matttproud/golang_protobuf_extensions c12348ce28de40eed0136aa2b644d0ee0650e56c # v1.0.1
|
||||||
github.com/Microsoft/go-winio 6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
|
github.com/Microsoft/go-winio 6c72808b55902eae4c5943626030429ff20f3b63 # v0.4.14
|
||||||
github.com/Microsoft/hcsshim 5bc557dd210ff2caf615e6e22d398123de77fc11 # v0.8.9
|
github.com/Microsoft/hcsshim 5bc557dd210ff2caf615e6e22d398123de77fc11 # v0.8.9
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2017 Yasuhiro Matsumoto
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
|
@ -1,47 +0,0 @@
|
||||||
# go-shellwords
|
|
||||||
|
|
||||||
[![codecov](https://codecov.io/gh/mattn/go-shellwords/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-shellwords)
|
|
||||||
[![Build Status](https://travis-ci.org/mattn/go-shellwords.svg?branch=master)](https://travis-ci.org/mattn/go-shellwords)
|
|
||||||
|
|
||||||
Parse line as shell words.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```go
|
|
||||||
args, err := shellwords.Parse("./foo --bar=baz")
|
|
||||||
// args should be ["./foo", "--bar=baz"]
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
os.Setenv("FOO", "bar")
|
|
||||||
p := shellwords.NewParser()
|
|
||||||
p.ParseEnv = true
|
|
||||||
args, err := p.Parse("./foo $FOO")
|
|
||||||
// args should be ["./foo", "bar"]
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
p := shellwords.NewParser()
|
|
||||||
p.ParseBacktick = true
|
|
||||||
args, err := p.Parse("./foo `echo $SHELL`")
|
|
||||||
// args should be ["./foo", "/bin/bash"]
|
|
||||||
```
|
|
||||||
|
|
||||||
```go
|
|
||||||
shellwords.ParseBacktick = true
|
|
||||||
p := shellwords.NewParser()
|
|
||||||
args, err := p.Parse("./foo `echo $SHELL`")
|
|
||||||
// args should be ["./foo", "/bin/bash"]
|
|
||||||
```
|
|
||||||
|
|
||||||
# Thanks
|
|
||||||
|
|
||||||
This is based on cpan module [Parse::CommandLine](https://metacpan.org/pod/Parse::CommandLine).
|
|
||||||
|
|
||||||
# License
|
|
||||||
|
|
||||||
under the MIT License: http://mattn.mit-license.org/2017
|
|
||||||
|
|
||||||
# Author
|
|
||||||
|
|
||||||
Yasuhiro Matsumoto (a.k.a mattn)
|
|
|
@ -1 +0,0 @@
|
||||||
module github.com/mattn/go-shellwords
|
|
|
@ -1,193 +0,0 @@
|
||||||
package shellwords
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ParseEnv bool = false
|
|
||||||
ParseBacktick bool = false
|
|
||||||
)
|
|
||||||
|
|
||||||
var envRe = regexp.MustCompile(`\$({[a-zA-Z0-9_]+}|[a-zA-Z0-9_]+)`)
|
|
||||||
|
|
||||||
func isSpace(r rune) bool {
|
|
||||||
switch r {
|
|
||||||
case ' ', '\t', '\r', '\n':
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func replaceEnv(getenv func(string) string, s string) string {
|
|
||||||
if getenv == nil {
|
|
||||||
getenv = os.Getenv
|
|
||||||
}
|
|
||||||
|
|
||||||
return envRe.ReplaceAllStringFunc(s, func(s string) string {
|
|
||||||
s = s[1:]
|
|
||||||
if s[0] == '{' {
|
|
||||||
s = s[1 : len(s)-1]
|
|
||||||
}
|
|
||||||
return getenv(s)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Parser struct {
|
|
||||||
ParseEnv bool
|
|
||||||
ParseBacktick bool
|
|
||||||
Position int
|
|
||||||
Dir string
|
|
||||||
|
|
||||||
// If ParseEnv is true, use this for getenv.
|
|
||||||
// If nil, use os.Getenv.
|
|
||||||
Getenv func(string) string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewParser() *Parser {
|
|
||||||
return &Parser{
|
|
||||||
ParseEnv: ParseEnv,
|
|
||||||
ParseBacktick: ParseBacktick,
|
|
||||||
Position: 0,
|
|
||||||
Dir: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) Parse(line string) ([]string, error) {
|
|
||||||
args := []string{}
|
|
||||||
buf := ""
|
|
||||||
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
|
|
||||||
backtick := ""
|
|
||||||
|
|
||||||
pos := -1
|
|
||||||
got := false
|
|
||||||
|
|
||||||
loop:
|
|
||||||
for i, r := range line {
|
|
||||||
if escaped {
|
|
||||||
buf += string(r)
|
|
||||||
escaped = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if r == '\\' {
|
|
||||||
if singleQuoted {
|
|
||||||
buf += string(r)
|
|
||||||
} else {
|
|
||||||
escaped = true
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if isSpace(r) {
|
|
||||||
if singleQuoted || doubleQuoted || backQuote || dollarQuote {
|
|
||||||
buf += string(r)
|
|
||||||
backtick += string(r)
|
|
||||||
} else if got {
|
|
||||||
if p.ParseEnv {
|
|
||||||
buf = replaceEnv(p.Getenv, buf)
|
|
||||||
}
|
|
||||||
args = append(args, buf)
|
|
||||||
buf = ""
|
|
||||||
got = false
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
switch r {
|
|
||||||
case '`':
|
|
||||||
if !singleQuoted && !doubleQuoted && !dollarQuote {
|
|
||||||
if p.ParseBacktick {
|
|
||||||
if backQuote {
|
|
||||||
out, err := shellRun(backtick, p.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buf = buf[:len(buf)-len(backtick)] + out
|
|
||||||
}
|
|
||||||
backtick = ""
|
|
||||||
backQuote = !backQuote
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
backtick = ""
|
|
||||||
backQuote = !backQuote
|
|
||||||
}
|
|
||||||
case ')':
|
|
||||||
if !singleQuoted && !doubleQuoted && !backQuote {
|
|
||||||
if p.ParseBacktick {
|
|
||||||
if dollarQuote {
|
|
||||||
out, err := shellRun(backtick, p.Dir)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
buf = buf[:len(buf)-len(backtick)-2] + out
|
|
||||||
}
|
|
||||||
backtick = ""
|
|
||||||
dollarQuote = !dollarQuote
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
backtick = ""
|
|
||||||
dollarQuote = !dollarQuote
|
|
||||||
}
|
|
||||||
case '(':
|
|
||||||
if !singleQuoted && !doubleQuoted && !backQuote {
|
|
||||||
if !dollarQuote && strings.HasSuffix(buf, "$") {
|
|
||||||
dollarQuote = true
|
|
||||||
buf += "("
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
return nil, errors.New("invalid command line string")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case '"':
|
|
||||||
if !singleQuoted && !dollarQuote {
|
|
||||||
doubleQuoted = !doubleQuoted
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case '\'':
|
|
||||||
if !doubleQuoted && !dollarQuote {
|
|
||||||
singleQuoted = !singleQuoted
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
case ';', '&', '|', '<', '>':
|
|
||||||
if !(escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote) {
|
|
||||||
if r == '>' && len(buf) > 0 {
|
|
||||||
if c := buf[0]; '0' <= c && c <= '9' {
|
|
||||||
i -= 1
|
|
||||||
got = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos = i
|
|
||||||
break loop
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
got = true
|
|
||||||
buf += string(r)
|
|
||||||
if backQuote || dollarQuote {
|
|
||||||
backtick += string(r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if got {
|
|
||||||
if p.ParseEnv {
|
|
||||||
buf = replaceEnv(p.Getenv, buf)
|
|
||||||
}
|
|
||||||
args = append(args, buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
if escaped || singleQuoted || doubleQuoted || backQuote || dollarQuote {
|
|
||||||
return nil, errors.New("invalid command line string")
|
|
||||||
}
|
|
||||||
|
|
||||||
p.Position = pos
|
|
||||||
|
|
||||||
return args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func Parse(line string) ([]string, error) {
|
|
||||||
return NewParser().Parse(line)
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
// +build !go1.6
|
|
||||||
|
|
||||||
package shellwords
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func shellRun(line, dir string) (string, error) {
|
|
||||||
var b []byte
|
|
||||||
var err error
|
|
||||||
var cmd *exec.Cmd
|
|
||||||
if runtime.GOOS == "windows" {
|
|
||||||
cmd = exec.Command(os.Getenv("COMSPEC"), "/c", line)
|
|
||||||
} else {
|
|
||||||
cmd = exec.Command(os.Getenv("SHELL"), "-c", line)
|
|
||||||
}
|
|
||||||
if dir != "" {
|
|
||||||
cmd.Dir = dir
|
|
||||||
}
|
|
||||||
b, err = cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(b)), nil
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
// +build !windows,go1.6
|
|
||||||
|
|
||||||
package shellwords
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func shellRun(line, dir string) (string, error) {
|
|
||||||
shell := os.Getenv("SHELL")
|
|
||||||
cmd := exec.Command(shell, "-c", line)
|
|
||||||
if dir != "" {
|
|
||||||
cmd.Dir = dir
|
|
||||||
}
|
|
||||||
b, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
if eerr, ok := err.(*exec.ExitError); ok {
|
|
||||||
b = eerr.Stderr
|
|
||||||
}
|
|
||||||
return "", errors.New(err.Error() + ":" + string(b))
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(b)), nil
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
// +build windows,go1.6
|
|
||||||
|
|
||||||
package shellwords
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func shellRun(line, dir string) (string, error) {
|
|
||||||
shell := os.Getenv("COMSPEC")
|
|
||||||
cmd := exec.Command(shell, "/c", line)
|
|
||||||
if dir != "" {
|
|
||||||
cmd.Dir = dir
|
|
||||||
}
|
|
||||||
b, err := cmd.Output()
|
|
||||||
if err != nil {
|
|
||||||
if eerr, ok := err.(*exec.ExitError); ok {
|
|
||||||
b = eerr.Stderr
|
|
||||||
}
|
|
||||||
return "", errors.New(err.Error() + ":" + string(b))
|
|
||||||
}
|
|
||||||
return strings.TrimSpace(string(b)), nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue