英文:
How docker-compose parse the `command` string?
问题
I ran the command:
docker-compose -p foo -f - up <<YAML
version: "3.8"
services:
main:
image: alpine
command: echo x && y
YAML
Expected
[+] Running 1/0
⠿ Container foo-main-1 Created 0.0s
Attaching to foo-main-1
foo-main-1 | x && y
foo-main-1 exited with code 0
Actual
[+] Running 1/0
⠿ Container foo-main-1 Created 0.0s
Attaching to foo-main-1
foo-main-1 | x
foo-main-1 exited with code 0
I know CMD
of Dockerfile
supports exec
form and shell form and expect docker-compose.yml
would behave like Dockerfile
, however, looks like docker-compose
uses some hacky way to pass the command
string.
command |
output |
---|---|
echo x y z | x y z |
echo x && y | x && y |
echo x | y | x |
echo x; y | x; y |
Question
- Does
docker-compose
simply use a regular expression to remove everything after;
,&&
, and|
? - If not, how does
docker-compose
process thecommand
string?
I would appreciate if you can also provide the source code reference of which line parsing command
string.
https://github.com/docker/compose
My Environment
λ docker -v
Docker version 20.10.22, build 3a2c30b
λ docker-compose -v
Docker Compose version v2.15.1
英文:
I ran the command:
docker-compose -p foo -f - up <<YAML
version: "3.8"
services:
main:
image: alpine
command: echo x && y
YAML
Expected
[+] Running 1/0
⠿ Container foo-main-1 Created 0.0s
Attaching to foo-main-1
foo-main-1 | x && y
foo-main-1 exited with code 0
Actual
[+] Running 1/0
⠿ Container foo-main-1 Created 0.0s
Attaching to foo-main-1
foo-main-1 | x
foo-main-1 exited with code 0
I know CMD
of Dockerfile
supports exec
form and shell form and expect docker-compose.yml
would behave like Dockerfile
, however, looks like docker-compose
uses some hacky way to pass the command
string.
command |
output |
---|---|
echo x y z | x y z |
echo x && y | x |
echo x | y | x |
echo x; y | x |
Question
- Does
docker-compose
simply use a regular expression to remove everything after;
,&&
and|
? - If not, how does
docker-compose
process thecommand
string?
I would appreciate if you can also provide the source code reference of which line parsing command
string.
https://github.com/docker/compose
My Environment
λ docker -v
Docker version 20.10.22, build 3a2c30b
λ docker-compose -v
Docker Compose version v2.15.1
答案1
得分: 2
Docker Compose V2已经用Go重写,它解析'command'的方式与V1不同。Docker Compose V2使用Go包'go-shellwords'来解析命令行,特定的特殊字符会被处理,比如:
\`)(";&|<>
对于字符串'echo x && y','&'字符会中断Parser()循环,导致字符串解析结束,输出结果为'echo x'。
此外,shell形式'echo x && y'表示'echo x',然后执行'y'。这可能不是您想要的,所以您可以明确声明命令字符串为'echo "x && y"'或'[ "sh", "-c", "echo x && y" ]',这取决于您的具体需求。
如果您想了解Docker Compose如何解析命令字符串的更多信息,可以参考源代码,特别是:
func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
...
project, err := cli.ProjectFromOptions(options)
if err != nil {
...
func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
...
project, err := loader.Load(types.ConfigDetails{
ConfigFiles: configs,
WorkingDir: workingDir,
Environment: options.Environment,
}, options.loadOptions...)
if err is not nil {
return nil, err
}
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
if str, ok := value.(string); ok {
return shellwords.Parse(str)
}
return value, nil
}
func (p *Parser) Parse(line string) ([]string, error) {
args := []
buf := ""
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
backtick := ""
...
您可以查看shellswords.go以了解Parse()函数如何解析命令字符串。
英文:
Docker Compose V2 has been rewritten in Go, and the way it parses 'command' is different from V1. Docker Compose V2 uses Go package 'go-shellwords' to parse the command line, with certain special characters being handled specifically, such as:
\`)(";&|<>
In the case of the string 'echo x && y', the '&' character breaks the Parser() loop and leads to the end of string parsing, resulting in the output terminating in 'echo x'.
Additionally, the shell form 'echo x && y' means 'echo x' and then execute 'y'. This may not be what you intended, so you can explicitly declare the command string as 'echo "x && y"' or '["sh", "-c", "echo x && y"]', depends on your specific needs.
If you're interested in learning more about how command strings are parsed by Docker Compose, you can refer to the source code, specifically:
func (o *ProjectOptions) ToProject(services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
...
project, err := cli.ProjectFromOptions(options)
if err != nil {
...
func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
...
project, err := loader.Load(types.ConfigDetails{
ConfigFiles: configs,
WorkingDir: workingDir,
Environment: options.Environment,
}, options.loadOptions...)
if err != nil {
return nil, err
}
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {
if str, ok := value.(string); ok {
return shellwords.Parse(str)
}
return value, nil
}
func (p *Parser) Parse(line string) ([]string, error) {
args := []string{}
buf := ""
var escaped, doubleQuoted, singleQuoted, backQuote, dollarQuote bool
backtick := ""
...
You can take a look at shellswords.go to see how command string is parsed by Parse() function.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论