소스 검색

docker ps: add fields for ordering and column selection

* api/client/ps.go: Refactor CmdPs to use a fields list of
  characters to determine which columns to print on `docker ps`
  invocation.

This adds an ability for the docker command to print the columns of
output in arbitrary order.

Signed-off-by: Jeff Mickey <j@codemac.net>

Docker-DCO-1.1-Signed-off-by: Jeff Mickey <j@codemac.net>
Jeff Mickey 10 년 전
부모
커밋
de0e883331
1개의 변경된 파일93개의 추가작업 그리고 43개의 파일을 삭제
  1. 93 43
      api/client/ps.go

+ 93 - 43
api/client/ps.go

@@ -38,6 +38,7 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		since    = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show created since Id or Name, include non-running")
 		since    = cmd.String([]string{"#sinceId", "#-since-id", "-since"}, "", "Show created since Id or Name, include non-running")
 		before   = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name")
 		before   = cmd.String([]string{"#beforeId", "#-before-id", "-before"}, "", "Show only container created before Id or Name")
 		last     = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running")
 		last     = cmd.Int([]string{"n"}, -1, "Show n last created containers, include non-running")
+		fields   = cmd.String([]string{"-fields"}, "cimtspn", "Choose fields to print, and order (c,i,m,t,s,p,n,z)")
 		flFilter = opts.NewListOpts(nil)
 		flFilter = opts.NewListOpts(nil)
 	)
 	)
 	cmd.Require(flag.Exact, 0)
 	cmd.Require(flag.Exact, 0)
@@ -99,13 +100,35 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 	}
 	}
 
 
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
 	w := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
+	if *quiet {
+		*fields = "c"
+	}
+
+	if *size {
+		*fields = *fields + "z"
+	}
+
 	if !*quiet {
 	if !*quiet {
-		fmt.Fprint(w, "CONTAINER ID\tIMAGE\tCOMMAND\tCREATED\tSTATUS\tPORTS\tNAMES")
+		headermap := map[rune]string{
+			'c': "CONTAINER ID",
+			'i': "IMAGE",
+			'm': "COMMAND",
+			's': "STATUS",
+			't': "CREATED",
+			'p': "PORTS",
+			'n': "NAMES",
+			'z': "SIZE",
+		}
 
 
-		if *size {
-			fmt.Fprintln(w, "\tSIZE")
-		} else {
-			fmt.Fprint(w, "\n")
+		headers := make([]string, 0)
+		for _, v := range *fields {
+			if title, ok := headermap[v]; ok {
+				headers = append(headers, title)
+			}
+		}
+
+		if len(headers) > 0 {
+			fmt.Fprint(w, strings.Join(headers, "\t")+"\n")
 		}
 		}
 	}
 	}
 
 
@@ -117,63 +140,90 @@ func (cli *DockerCli) CmdPs(args ...string) error {
 		return ss
 		return ss
 	}
 	}
 
 
-	for _, container := range containers {
-		ID := container.ID
-
-		if !*noTrunc {
-			ID = stringid.TruncateID(ID)
-		}
+	type containerMeta struct {
+		c string
+		i string
+		m string
+		t string
+		s string
+		p string
+		n string
+		z string
+	}
 
 
-		if *quiet {
-			fmt.Fprintln(w, ID)
+	var displayPort string
+	if container.HostConfig.NetworkMode == "host" {
+		displayPort = "*/tcp, */udp"
+	} else {
+		displayPort = api.DisplayablePorts(container.Ports)
+	}
 
 
-			continue
+	outp := make([]containerMeta, 0)
+	for _, container := range containers {
+		next := containerMeta{
+			c: container.ID,
+			n: "",
+			m: strconv.Quote(container.Command),
+			i: container.Image,
+			t: units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(container.Created), 0))) + " ago",
+			s: container.Status,
+			p: displayPort,
+			z: fmt.Sprintf("%s", units.HumanSize(float64(container.SizeRw))),
 		}
 		}
 
 
-		var (
-			names       = stripNamePrefix(container.Names)
-			command     = strconv.Quote(container.Command)
-			displayPort string
-		)
-
+		// handle truncation
+		outNames := stripNamePrefix(container.Names)
 		if !*noTrunc {
 		if !*noTrunc {
-			command = stringutils.Truncate(command, 20)
-
+			next.c = stringid.TruncateID(next.c)
+			next.m = stringutils.Truncate(next.m, 20)
 			// only display the default name for the container with notrunc is passed
 			// only display the default name for the container with notrunc is passed
-			for _, name := range names {
+			for _, name := range outNames {
 				if len(strings.Split(name, "/")) == 1 {
 				if len(strings.Split(name, "/")) == 1 {
-					names = []string{name}
+					outNames = []string{name}
 					break
 					break
 				}
 				}
 			}
 			}
 		}
 		}
+		next.n = strings.Join(outNames, ",")
 
 
-		image := container.Image
-		if image == "" {
-			image = "<no image>"
+		if next.i == "" {
+			next.i = "<no image>"
 		}
 		}
 
 
-		if container.HostConfig.NetworkMode == "host" {
-			displayPort = "*/tcp, */udp"
-		} else {
-			displayPort = api.DisplayablePorts(container.Ports)
+		// handle rootfs sizing
+		if container.SizeRootFs > 0 {
+			next.z = next.z + fmt.Sprintf(" (virtual %s)", units.HumanSize(float64(container.SizeRootFs)))
 		}
 		}
 
 
-		fmt.Fprintf(w, "%s\t%s\t%s\t%s ago\t%s\t%s\t%s\t", ID, image, command,
-			units.HumanDuration(time.Now().UTC().Sub(time.Unix(int64(container.Created), 0))),
-			container.Status, displayPort, strings.Join(names, ","))
+		outp = append(outp, next)
+	}
 
 
-		if *size {
-			if container.SizeRootFs > 0 {
-				fmt.Fprintf(w, "%s (virtual %s)\n", units.HumanSize(float64(container.SizeRw)), units.HumanSize(float64(container.SizeRootFs)))
-			} else {
-				fmt.Fprintf(w, "%s\n", units.HumanSize(float64(container.SizeRw)))
-			}
+	for _, out := range outp {
+		of := make([]string, 0)
+		for _, v := range *fields {
+			switch v {
+			case 'c':
+				of = append(of, out.c)
+			case 'i':
+				of = append(of, out.i)
+			case 'm':
+				of = append(of, out.m)
+			case 't':
+				of = append(of, out.t)
+			case 's':
+				of = append(of, out.s)
+			case 'p':
+				of = append(of, out.p)
+			case 'n':
+				of = append(of, out.n)
+			case 'z':
+				of = append(of, out.z)
 
 
-			continue
+			}
+		}
+		if len(of) > 0 {
+			fmt.Fprintf(w, "%s\n", strings.Join(of, "\t"))
 		}
 		}
-
-		fmt.Fprint(w, "\n")
 	}
 	}
 
 
 	if !*quiet {
 	if !*quiet {