diff --git a/api/client/cli.go b/api/client/cli.go index d80f9cc32c..fa3be2d8d9 100644 --- a/api/client/cli.go +++ b/api/client/cli.go @@ -63,7 +63,11 @@ func (cli *DockerCli) Cmd(args ...string) error { func (cli *DockerCli) Subcmd(name, signature, description string) *flag.FlagSet { flags := flag.NewFlagSet(name, flag.ContinueOnError) flags.Usage = func() { - fmt.Fprintf(cli.err, "\nUsage: docker %s %s\n\n%s\n\n", name, signature, description) + options := "" + if flags.FlagCountUndeprecated() > 0 { + options = "[OPTIONS] " + } + fmt.Fprintf(cli.err, "\nUsage: docker %s %s%s\n\n%s\n\n", name, options, signature, description) flags.PrintDefaults() os.Exit(2) } diff --git a/api/client/commands.go b/api/client/commands.go index cb60f6980c..cd954b92e2 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -98,7 +98,7 @@ func (cli *DockerCli) CmdHelp(args ...string) error { } func (cli *DockerCli) CmdBuild(args ...string) error { - cmd := cli.Subcmd("build", "[OPTIONS] PATH | URL | -", "Build a new image from the source code at PATH") + cmd := cli.Subcmd("build", "PATH | URL | -", "Build a new image from the source code at PATH") tag := cmd.String([]string{"t", "-tag"}, "", "Repository name (and optionally a tag) to be applied to the resulting image in case of success") suppressOutput := cmd.Bool([]string{"q", "-quiet"}, false, "Suppress the verbose output generated by the containers") noCache := cmd.Bool([]string{"#no-cache", "-no-cache"}, false, "Do not use cache when building the image") @@ -255,7 +255,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error { // 'docker login': login / register a user to registry service. func (cli *DockerCli) CmdLogin(args ...string) error { - cmd := cli.Subcmd("login", "[OPTIONS] [SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.") + cmd := cli.Subcmd("login", "[SERVER]", "Register or log in to a Docker registry server, if no server is specified \""+registry.IndexServerAddress()+"\" is the default.") var username, password, email string @@ -529,7 +529,7 @@ func (cli *DockerCli) CmdInfo(args ...string) error { } func (cli *DockerCli) CmdStop(args ...string) error { - cmd := cli.Subcmd("stop", "[OPTIONS] CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period") + cmd := cli.Subcmd("stop", "CONTAINER [CONTAINER...]", "Stop a running container by sending SIGTERM and then SIGKILL after a grace period") nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to wait for the container to stop before killing it. Default is 10 seconds.") if err := cmd.Parse(args); err != nil { return nil @@ -556,7 +556,7 @@ func (cli *DockerCli) CmdStop(args ...string) error { } func (cli *DockerCli) CmdRestart(args ...string) error { - cmd := cli.Subcmd("restart", "[OPTIONS] CONTAINER [CONTAINER...]", "Restart a running container") + cmd := cli.Subcmd("restart", "CONTAINER [CONTAINER...]", "Restart a running container") nSeconds := cmd.Int([]string{"t", "-time"}, 10, "Number of seconds to try to stop for before killing the container. Once killed it will then be restarted. Default is 10 seconds.") if err := cmd.Parse(args); err != nil { return nil @@ -954,7 +954,7 @@ func (cli *DockerCli) CmdRmi(args ...string) error { } func (cli *DockerCli) CmdHistory(args ...string) error { - cmd := cli.Subcmd("history", "[OPTIONS] IMAGE", "Show the history of an image") + cmd := cli.Subcmd("history", "IMAGE", "Show the history of an image") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") @@ -1011,7 +1011,7 @@ func (cli *DockerCli) CmdHistory(args ...string) error { } func (cli *DockerCli) CmdRm(args ...string) error { - cmd := cli.Subcmd("rm", "[OPTIONS] CONTAINER [CONTAINER...]", "Remove one or more containers") + cmd := cli.Subcmd("rm", "CONTAINER [CONTAINER...]", "Remove one or more containers") v := cmd.Bool([]string{"v", "-volumes"}, false, "Remove the volumes associated with the container") link := cmd.Bool([]string{"l", "#link", "-link"}, false, "Remove the specified link and not the underlying container") force := cmd.Bool([]string{"f", "-force"}, false, "Force the removal of a running container (uses SIGKILL)") @@ -1051,7 +1051,7 @@ func (cli *DockerCli) CmdRm(args ...string) error { // 'docker kill NAME' kills a running container func (cli *DockerCli) CmdKill(args ...string) error { - cmd := cli.Subcmd("kill", "[OPTIONS] CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal") + cmd := cli.Subcmd("kill", "CONTAINER [CONTAINER...]", "Kill a running container using SIGKILL or a specified signal") signal := cmd.String([]string{"s", "-signal"}, "KILL", "Signal to send to the container") if err := cmd.Parse(args); err != nil { @@ -1245,7 +1245,7 @@ func (cli *DockerCli) CmdPull(args ...string) error { } func (cli *DockerCli) CmdImages(args ...string) error { - cmd := cli.Subcmd("images", "[OPTIONS] [NAME]", "List images") + cmd := cli.Subcmd("images", "[NAME]", "List images") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only show numeric IDs") all := cmd.Bool([]string{"a", "-all"}, false, "Show all images (by default filter out the intermediate image layers)") noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Don't truncate output") @@ -1476,7 +1476,7 @@ func (cli *DockerCli) printTreeNode(noTrunc bool, image *engine.Env, prefix stri } func (cli *DockerCli) CmdPs(args ...string) error { - cmd := cli.Subcmd("ps", "[OPTIONS]", "List containers") + cmd := cli.Subcmd("ps", "", "List containers") quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") size := cmd.Bool([]string{"s", "-size"}, false, "Display sizes") all := cmd.Bool([]string{"a", "-all"}, false, "Show all containers. Only running containers are shown by default.") @@ -1598,7 +1598,7 @@ func (cli *DockerCli) CmdPs(args ...string) error { } func (cli *DockerCli) CmdCommit(args ...string) error { - cmd := cli.Subcmd("commit", "[OPTIONS] CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes") + cmd := cli.Subcmd("commit", "CONTAINER [REPOSITORY[:TAG]]", "Create a new image from a container's changes") flPause := cmd.Bool([]string{"p", "-pause"}, true, "Pause container during commit") flComment := cmd.String([]string{"m", "-message"}, "", "Commit message") flAuthor := cmd.String([]string{"a", "#author", "-author"}, "", "Author (e.g., \"John Hannibal Smith \")") @@ -1659,7 +1659,7 @@ func (cli *DockerCli) CmdCommit(args ...string) error { } func (cli *DockerCli) CmdEvents(args ...string) error { - cmd := cli.Subcmd("events", "[OPTIONS]", "Get real time events from the server") + cmd := cli.Subcmd("events", "", "Get real time events from the server") since := cmd.String([]string{"#since", "-since"}, "", "Show all events created since timestamp") until := cmd.String([]string{"-until"}, "", "Stream events until this timestamp") if err := cmd.Parse(args); err != nil { @@ -1795,7 +1795,7 @@ func (cli *DockerCli) CmdLogs(args ...string) error { func (cli *DockerCli) CmdAttach(args ...string) error { var ( - cmd = cli.Subcmd("attach", "[OPTIONS] CONTAINER", "Attach to a running container") + cmd = cli.Subcmd("attach", "CONTAINER", "Attach to a running container") noStdin = cmd.Bool([]string{"#nostdin", "-no-stdin"}, false, "Do not attach STDIN") proxy = cmd.Bool([]string{"#sig-proxy", "-sig-proxy"}, true, "Proxy all received signals to the process (even in non-TTY mode). SIGCHLD, SIGKILL, and SIGSTOP are not proxied.") ) @@ -1923,7 +1923,7 @@ func (cli *DockerCli) CmdSearch(args ...string) error { type ports []int func (cli *DockerCli) CmdTag(args ...string) error { - cmd := cli.Subcmd("tag", "[OPTIONS] IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository") + cmd := cli.Subcmd("tag", "IMAGE[:TAG] [REGISTRYHOST/][USERNAME/]NAME[:TAG]", "Tag an image into a repository") force := cmd.Bool([]string{"f", "#force", "-force"}, false, "Force") if err := cmd.Parse(args); err != nil { return nil @@ -1992,7 +1992,7 @@ func (cli *DockerCli) pullImage(image string) error { func (cli *DockerCli) CmdRun(args ...string) error { // FIXME: just use runconfig.Parse already - config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "[OPTIONS] IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil) + config, hostConfig, cmd, err := runconfig.ParseSubcommand(cli.Subcmd("run", "IMAGE [COMMAND] [ARG...]", "Run a command in a new container"), args, nil) if err != nil { return err } diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index bae418d10b..f67527b380 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -625,7 +625,7 @@ ensure we know how your setup is configured. ## inspect - Usage: docker inspect CONTAINER|IMAGE [CONTAINER|IMAGE...] + Usage: docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...] Return low-level information on a container or image @@ -690,7 +690,7 @@ signal specified with option `--signal`. ## load - Usage: docker load + Usage: docker load [OPTIONS] Load an image from a tar archive on STDIN @@ -742,7 +742,7 @@ For example: ## logs - Usage: docker logs CONTAINER + Usage: docker logs [OPTIONS] CONTAINER Fetch the logs of a container @@ -832,7 +832,7 @@ This shows all the containers that have exited with status of '0' ## pull - Usage: docker pull NAME[:TAG] + Usage: docker pull [OPTIONS] NAME[:TAG] Pull an image or a repository from the registry @@ -922,7 +922,7 @@ delete them. Any running containers will not be deleted. ## rmi - Usage: docker rmi IMAGE [IMAGE...] + Usage: docker rmi [OPTIONS] IMAGE [IMAGE...] Remove one or more images @@ -1249,7 +1249,7 @@ Providing a maximum restart limit is only valid for the ** on-failure ** policy. ## save - Usage: docker save IMAGE + Usage: docker save [OPTIONS] IMAGE Save an image to a tar archive (streamed to STDOUT by default) @@ -1274,7 +1274,7 @@ It is used to create a backup that can then be used with Search [Docker Hub](https://hub.docker.com) for images - Usage: docker search TERM + Usage: docker search [OPTIONS] TERM Search the Docker Hub for images @@ -1288,7 +1288,7 @@ more details on finding shared images from the command line. ## start - Usage: docker start CONTAINER [CONTAINER...] + Usage: docker start [OPTIONS] CONTAINER [CONTAINER...] Restart a stopped container diff --git a/pkg/mflag/flag.go b/pkg/mflag/flag.go index 6e3f039707..0eebfc18f1 100644 --- a/pkg/mflag/flag.go +++ b/pkg/mflag/flag.go @@ -472,6 +472,23 @@ var Usage = func() { PrintDefaults() } +// FlagCount returns the number of flags that have been defined. +func (f *FlagSet) FlagCount() int { return len(f.formal) } + +// FlagCountUndeprecated returns the number of undeprecated flags that have been defined. +func (f *FlagSet) FlagCountUndeprecated() int { + count := 0 + for _, flag := range sortFlags(f.formal) { + for _, name := range flag.Names { + if name[0] != '#' { + count++ + break + } + } + } + return count +} + // NFlag returns the number of flags that have been set. func (f *FlagSet) NFlag() int { return len(f.actual) } diff --git a/pkg/mflag/flag_test.go b/pkg/mflag/flag_test.go index 2aa6fdab00..9321926c56 100644 --- a/pkg/mflag/flag_test.go +++ b/pkg/mflag/flag_test.go @@ -428,3 +428,32 @@ func TestHelp(t *testing.T) { t.Fatal("help was called; should not have been for defined help flag") } } + +// Test the flag count functions. +func TestFlagCounts(t *testing.T) { + fs := NewFlagSet("help test", ContinueOnError) + var flag bool + fs.BoolVar(&flag, []string{"flag1"}, false, "regular flag") + fs.BoolVar(&flag, []string{"#deprecated1"}, false, "regular flag") + fs.BoolVar(&flag, []string{"f", "flag2"}, false, "regular flag") + fs.BoolVar(&flag, []string{"#d", "#deprecated2"}, false, "regular flag") + fs.BoolVar(&flag, []string{"flag3"}, false, "regular flag") + fs.BoolVar(&flag, []string{"g", "#flag4", "-flag4"}, false, "regular flag") + + if fs.FlagCount() != 10 { + t.Fatal("FlagCount wrong. ", fs.FlagCount()) + } + if fs.FlagCountUndeprecated() != 4 { + t.Fatal("FlagCountUndeprecated wrong. ", fs.FlagCountUndeprecated()) + } + if fs.NFlag() != 0 { + t.Fatal("NFlag wrong. ", fs.NFlag()) + } + err := fs.Parse([]string{"-fd", "-g", "-flag4"}) + if err != nil { + t.Fatal("expected no error for defined -help; got ", err) + } + if fs.NFlag() != 4 { + t.Fatal("NFlag wrong. ", fs.NFlag()) + } +}