1cli v2 manual
2===
3
4<!-- toc -->
5
6- [Migrating From Older Releases](#migrating-from-older-releases)
7- [Getting Started](#getting-started)
8- [Examples](#examples)
9  * [Arguments](#arguments)
10  * [Flags](#flags)
11    + [Placeholder Values](#placeholder-values)
12    + [Alternate Names](#alternate-names)
13    + [Ordering](#ordering)
14    + [Values from the Environment](#values-from-the-environment)
15    + [Values from files](#values-from-files)
16    + [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
17    + [Required Flags](#required-flags)
18    + [Default Values for help output](#default-values-for-help-output)
19    + [Precedence](#precedence)
20  * [Subcommands](#subcommands)
21  * [Subcommands categories](#subcommands-categories)
22  * [Exit code](#exit-code)
23  * [Combining short options](#combining-short-options)
24  * [Bash Completion](#bash-completion)
25    + [Default auto-completion](#default-auto-completion)
26    + [Custom auto-completion](#custom-auto-completion)
27    + [Enabling](#enabling)
28    + [Distribution and Persistent Autocompletion](#distribution-and-persistent-autocompletion)
29    + [Customization](#customization)
30    + [ZSH Support](#zsh-support)
31    + [ZSH default auto-complete example](#zsh-default-auto-complete-example)
32    + [ZSH custom auto-complete example](#zsh-custom-auto-complete-example)
33  * [Generated Help Text](#generated-help-text)
34    + [Customization](#customization-1)
35  * [Version Flag](#version-flag)
36    + [Customization](#customization-2)
37  * [Timestamp Flag](#timestamp-flag)
38  * [Full API Example](#full-api-example)
39
40<!-- tocstop -->
41
42## Migrating From Older Releases
43
44There are a small set of breaking changes between v1 and v2.
45Converting is relatively straightforward and typically takes less than
46an hour. Specific steps are included in
47[Migration Guide: v1 to v2](../migrate-v1-to-v2.md).
48
49## Getting Started
50
51One of the philosophies behind cli is that an API should be playful and full of
52discovery. So a cli app can be as little as one line of code in `main()`.
53
54<!-- {
55  "args": ["&#45;&#45;help"],
56  "output": "A new cli application"
57} -->
58``` go
59package main
60
61import (
62  "os"
63
64  "github.com/urfave/cli/v2"
65)
66
67func main() {
68  (&cli.App{}).Run(os.Args)
69}
70```
71
72This app will run and show help text, but is not very useful. Let's give an
73action to execute and some help documentation:
74
75<!-- {
76  "output": "boom! I say!"
77} -->
78``` go
79package main
80
81import (
82  "fmt"
83  "log"
84  "os"
85
86  "github.com/urfave/cli/v2"
87)
88
89func main() {
90  app := &cli.App{
91    Name: "boom",
92    Usage: "make an explosive entrance",
93    Action: func(c *cli.Context) error {
94      fmt.Println("boom! I say!")
95      return nil
96    },
97  }
98
99  err := app.Run(os.Args)
100  if err != nil {
101    log.Fatal(err)
102  }
103}
104```
105
106Running this already gives you a ton of functionality, plus support for things
107like subcommands and flags, which are covered below.
108
109## Examples
110
111Being a programmer can be a lonely job. Thankfully by the power of automation
112that is not the case! Let's create a greeter app to fend off our demons of
113loneliness!
114
115Start by creating a directory named `greet`, and within it, add a file,
116`greet.go` with the following code in it:
117
118<!-- {
119  "output": "Hello friend!"
120} -->
121``` go
122package main
123
124import (
125  "fmt"
126  "log"
127  "os"
128
129  "github.com/urfave/cli/v2"
130)
131
132func main() {
133  app := &cli.App{
134    Name: "greet",
135    Usage: "fight the loneliness!",
136    Action: func(c *cli.Context) error {
137      fmt.Println("Hello friend!")
138      return nil
139    },
140  }
141
142  err := app.Run(os.Args)
143  if err != nil {
144    log.Fatal(err)
145  }
146}
147```
148
149Install our command to the `$GOPATH/bin` directory:
150
151```
152$ go install
153```
154
155Finally run our new command:
156
157```
158$ greet
159Hello friend!
160```
161
162cli also generates neat help text:
163
164```
165$ greet help
166NAME:
167    greet - fight the loneliness!
168
169USAGE:
170    greet [global options] command [command options] [arguments...]
171
172COMMANDS:
173    help, h  Shows a list of commands or help for one command
174
175GLOBAL OPTIONS
176    --help, -h  show help (default: false)
177```
178
179### Arguments
180
181You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
182
183<!-- {
184  "output": "Hello \""
185} -->
186``` go
187package main
188
189import (
190  "fmt"
191  "log"
192  "os"
193
194  "github.com/urfave/cli/v2"
195)
196
197func main() {
198  app := &cli.App{
199    Action: func(c *cli.Context) error {
200      fmt.Printf("Hello %q", c.Args().Get(0))
201      return nil
202    },
203  }
204
205  err := app.Run(os.Args)
206  if err != nil {
207    log.Fatal(err)
208  }
209}
210```
211
212### Flags
213
214Setting and querying flags is simple.
215
216<!-- {
217  "output": "Hello Nefertiti"
218} -->
219``` go
220package main
221
222import (
223  "fmt"
224  "log"
225  "os"
226
227  "github.com/urfave/cli/v2"
228)
229
230func main() {
231  app := &cli.App{
232    Flags: []cli.Flag {
233      &cli.StringFlag{
234        Name: "lang",
235        Value: "english",
236        Usage: "language for the greeting",
237      },
238    },
239    Action: func(c *cli.Context) error {
240      name := "Nefertiti"
241      if c.NArg() > 0 {
242        name = c.Args().Get(0)
243      }
244      if c.String("lang") == "spanish" {
245        fmt.Println("Hola", name)
246      } else {
247        fmt.Println("Hello", name)
248      }
249      return nil
250    },
251  }
252
253  err := app.Run(os.Args)
254  if err != nil {
255    log.Fatal(err)
256  }
257}
258```
259
260You can also set a destination variable for a flag, to which the content will be
261scanned.
262
263<!-- {
264  "output": "Hello someone"
265} -->
266``` go
267package main
268
269import (
270  "log"
271  "os"
272  "fmt"
273
274  "github.com/urfave/cli/v2"
275)
276
277func main() {
278  var language string
279
280  app := &cli.App{
281    Flags: []cli.Flag {
282      &cli.StringFlag{
283        Name:        "lang",
284        Value:       "english",
285        Usage:       "language for the greeting",
286        Destination: &language,
287      },
288    },
289    Action: func(c *cli.Context) error {
290      name := "someone"
291      if c.NArg() > 0 {
292        name = c.Args().Get(0)
293      }
294      if language == "spanish" {
295        fmt.Println("Hola", name)
296      } else {
297        fmt.Println("Hello", name)
298      }
299      return nil
300    },
301  }
302
303  err := app.Run(os.Args)
304  if err != nil {
305    log.Fatal(err)
306  }
307}
308```
309
310See full list of flags at https://pkg.go.dev/github.com/urfave/cli/v2
311
312#### Placeholder Values
313
314Sometimes it's useful to specify a flag's value within the usage string itself.
315Such placeholders are indicated with back quotes.
316
317For example this:
318
319<!-- {
320  "args": ["&#45;&#45;help"],
321  "output": "&#45;&#45;config FILE, &#45;c FILE"
322} -->
323```go
324package main
325
326import (
327  "log"
328  "os"
329
330  "github.com/urfave/cli/v2"
331)
332
333func main() {
334  app := &cli.App{
335    Flags: []cli.Flag{
336      &cli.StringFlag{
337        Name:    "config",
338        Aliases: []string{"c"},
339        Usage:   "Load configuration from `FILE`",
340      },
341    },
342  }
343
344  err := app.Run(os.Args)
345  if err != nil {
346    log.Fatal(err)
347  }
348}
349```
350
351Will result in help output like:
352
353```
354--config FILE, -c FILE   Load configuration from FILE
355```
356
357Note that only the first placeholder is used. Subsequent back-quoted words will
358be left as-is.
359
360#### Alternate Names
361
362You can set alternate (or short) names for flags by providing a comma-delimited
363list for the `Name`. e.g.
364
365<!-- {
366  "args": ["&#45;&#45;help"],
367  "output": "&#45;&#45;lang value, &#45;l value.*language for the greeting.*default: \"english\""
368} -->
369``` go
370package main
371
372import (
373  "log"
374  "os"
375
376  "github.com/urfave/cli/v2"
377)
378
379func main() {
380  app := &cli.App{
381    Flags: []cli.Flag {
382      &cli.StringFlag{
383        Name:    "lang",
384        Aliases: []string{"l"},
385        Value:   "english",
386        Usage:   "language for the greeting",
387      },
388    },
389  }
390
391  err := app.Run(os.Args)
392  if err != nil {
393    log.Fatal(err)
394  }
395}
396```
397
398That flag can then be set with `--lang spanish` or `-l spanish`. Note that
399giving two different forms of the same flag in the same command invocation is an
400error.
401
402#### Ordering
403
404Flags for the application and commands are shown in the order they are defined.
405However, it's possible to sort them from outside this library by using `FlagsByName`
406or `CommandsByName` with `sort`.
407
408For example this:
409
410<!-- {
411  "args": ["&#45;&#45;help"],
412  "output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
413} -->
414``` go
415package main
416
417import (
418  "log"
419  "os"
420  "sort"
421
422  "github.com/urfave/cli/v2"
423)
424
425func main() {
426  app := &cli.App{
427    Flags: []cli.Flag{
428      &cli.StringFlag{
429        Name:  "lang, l",
430        Value: "english",
431        Usage: "Language for the greeting",
432      },
433      &cli.StringFlag{
434        Name:  "config, c",
435        Usage: "Load configuration from `FILE`",
436      },
437    },
438    Commands: []*cli.Command{
439      {
440        Name:    "complete",
441        Aliases: []string{"c"},
442        Usage:   "complete a task on the list",
443        Action:  func(c *cli.Context) error {
444          return nil
445        },
446      },
447      {
448        Name:    "add",
449        Aliases: []string{"a"},
450        Usage:   "add a task to the list",
451        Action:  func(c *cli.Context) error {
452          return nil
453        },
454      },
455    },
456  }
457
458  sort.Sort(cli.FlagsByName(app.Flags))
459  sort.Sort(cli.CommandsByName(app.Commands))
460
461  err := app.Run(os.Args)
462  if err != nil {
463    log.Fatal(err)
464  }
465}
466```
467
468Will result in help output like:
469
470```
471--config FILE, -c FILE  Load configuration from FILE
472--lang value, -l value  Language for the greeting (default: "english")
473```
474
475#### Values from the Environment
476
477You can also have the default value set from the environment via `EnvVars`.  e.g.
478
479<!-- {
480  "args": ["&#45;&#45;help"],
481  "output": "language for the greeting.*APP_LANG"
482} -->
483``` go
484package main
485
486import (
487  "log"
488  "os"
489
490  "github.com/urfave/cli/v2"
491)
492
493func main() {
494  app := &cli.App{
495    Flags: []cli.Flag {
496      &cli.StringFlag{
497        Name:    "lang",
498        Aliases: []string{"l"},
499        Value:   "english",
500        Usage:   "language for the greeting",
501        EnvVars: []string{"APP_LANG"},
502      },
503    },
504  }
505
506  err := app.Run(os.Args)
507  if err != nil {
508    log.Fatal(err)
509  }
510}
511```
512
513If `EnvVars` contains more than one string, the first environment variable that
514resolves is used as the default.
515
516<!-- {
517  "args": ["&#45;&#45;help"],
518  "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
519} -->
520``` go
521package main
522
523import (
524  "log"
525  "os"
526
527  "github.com/urfave/cli/v2"
528)
529
530func main() {
531  app := &cli.App{
532    Flags: []cli.Flag{
533      &cli.StringFlag{
534        Name:    "lang",
535        Aliases: []string{"l"},
536        Value:   "english",
537        Usage:   "language for the greeting",
538        EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"},
539      },
540    },
541  }
542
543  err := app.Run(os.Args)
544  if err != nil {
545    log.Fatal(err)
546  }
547}
548```
549
550#### Values from files
551
552You can also have the default value set from file via `FilePath`.  e.g.
553
554<!-- {
555  "args": ["&#45;&#45;help"],
556  "output": "password for the mysql database"
557} -->
558``` go
559package main
560
561import (
562  "log"
563  "os"
564
565  "github.com/urfave/cli/v2"
566)
567
568func main() {
569  app := cli.NewApp()
570
571  app.Flags = []cli.Flag {
572    &cli.StringFlag{
573      Name: "password, p",
574      Usage: "password for the mysql database",
575      FilePath: "/etc/mysql/password",
576    },
577  }
578
579  err := app.Run(os.Args)
580  if err != nil {
581    log.Fatal(err)
582  }
583}
584```
585
586Note that default values set from file (e.g. `FilePath`) take precedence over
587default values set from the environment (e.g. `EnvVar`).
588
589#### Values from alternate input sources (YAML, TOML, and others)
590
591There is a separate package altsrc that adds support for getting flag values
592from other file input sources.
593
594Currently supported input source formats:
595* YAML
596* JSON
597* TOML
598
599In order to get values for a flag from an alternate input source the following
600code would be added to wrap an existing cli.Flag like below:
601
602``` go
603  altsrc.NewIntFlag(&cli.IntFlag{Name: "test"})
604```
605
606Initialization must also occur for these flags. Below is an example initializing
607getting data from a yaml file below.
608
609``` go
610  command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load"))
611```
612
613The code above will use the "load" string as a flag name to get the file name of
614a yaml file from the cli.Context.  It will then use that file name to initialize
615the yaml input source for any flags that are defined on that command.  As a note
616the "load" flag used would also have to be defined on the command flags in order
617for this code snippet to work.
618
619Currently only YAML, JSON, and TOML files are supported but developers can add support
620for other input sources by implementing the altsrc.InputSourceContext for their
621given sources.
622
623Here is a more complete sample of a command using YAML support:
624
625<!-- {
626  "args": ["test-cmd", "&#45;&#45;help"],
627  "output": "&#45&#45;test value.*default: 0"
628} -->
629``` go
630package main
631
632import (
633  "fmt"
634  "os"
635
636  "github.com/urfave/cli/v2"
637  "github.com/urfave/cli/v2/altsrc"
638)
639
640func main() {
641  flags := []cli.Flag{
642    altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}),
643    &cli.StringFlag{Name: "load"},
644  }
645
646  app := &cli.App{
647    Action: func(c *cli.Context) error {
648      fmt.Println("yaml ist rad")
649      return nil
650    },
651    Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")),
652    Flags: flags,
653  }
654
655  app.Run(os.Args)
656}
657```
658
659#### Required Flags
660
661You can make a flag required by setting the `Required` field to `true`. If a user
662does not provide a required flag, they will be shown an error message.
663
664Take for example this app that requires the `lang` flag:
665
666<!-- {
667  "error": "Required flag \"lang\" not set"
668} -->
669```go
670package main
671
672import (
673  "log"
674  "os"
675  "github.com/urfave/cli/v2"
676)
677
678func main() {
679  app := cli.NewApp()
680
681  app.Flags = []cli.Flag {
682    &cli.StringFlag{
683      Name: "lang",
684      Value: "english",
685      Usage: "language for the greeting",
686      Required: true,
687    },
688  }
689
690  app.Action = func(c *cli.Context) error {
691    var output string
692    if c.String("lang") == "spanish" {
693      output = "Hola"
694    } else {
695      output = "Hello"
696    }
697    fmt.Println(output)
698    return nil
699  }
700
701  err := app.Run(os.Args)
702  if err != nil {
703    log.Fatal(err)
704  }
705}
706```
707
708If the app is run without the `lang` flag, the user will see the following message
709
710```
711Required flag "lang" not set
712```
713
714#### Default Values for help output
715
716Sometimes it's useful to specify a flag's default help-text value within the flag declaration. This can be useful if the default value for a flag is a computed value. The default value can be set via the `DefaultText` struct field.
717
718For example this:
719
720<!-- {
721  "args": ["&#45;&#45;help"],
722  "output": "&#45;&#45;port value"
723} -->
724```go
725package main
726
727import (
728  "log"
729  "os"
730
731  "github.com/urfave/cli/v2"
732)
733
734func main() {
735  app := &cli.App{
736    Flags: []cli.Flag{
737      &cli.IntFlag{
738        Name:    "port",
739        Usage:   "Use a randomized port",
740        Value: 0,
741        DefaultText: "random",
742      },
743    },
744  }
745
746  err := app.Run(os.Args)
747  if err != nil {
748    log.Fatal(err)
749  }
750}
751```
752
753Will result in help output like:
754
755```
756--port value  Use a randomized port (default: random)
757```
758
759#### Precedence
760
761The precedence for flag value sources is as follows (highest to lowest):
762
7630. Command line flag value from user
7640. Environment variable (if specified)
7650. Configuration file (if specified)
7660. Default defined on the flag
767
768### Subcommands
769
770Subcommands can be defined for a more git-like command line app.
771
772<!-- {
773  "args": ["template", "add"],
774  "output": "new task template: .+"
775} -->
776```go
777package main
778
779import (
780  "fmt"
781  "log"
782  "os"
783
784  "github.com/urfave/cli/v2"
785)
786
787func main() {
788  app := &cli.App{
789    Commands: []*cli.Command{
790      {
791        Name:    "add",
792        Aliases: []string{"a"},
793        Usage:   "add a task to the list",
794        Action:  func(c *cli.Context) error {
795          fmt.Println("added task: ", c.Args().First())
796          return nil
797        },
798      },
799      {
800        Name:    "complete",
801        Aliases: []string{"c"},
802        Usage:   "complete a task on the list",
803        Action:  func(c *cli.Context) error {
804          fmt.Println("completed task: ", c.Args().First())
805          return nil
806        },
807      },
808      {
809        Name:        "template",
810        Aliases:     []string{"t"},
811        Usage:       "options for task templates",
812        Subcommands: []*cli.Command{
813          {
814            Name:  "add",
815            Usage: "add a new template",
816            Action: func(c *cli.Context) error {
817              fmt.Println("new task template: ", c.Args().First())
818              return nil
819            },
820          },
821          {
822            Name:  "remove",
823            Usage: "remove an existing template",
824            Action: func(c *cli.Context) error {
825              fmt.Println("removed task template: ", c.Args().First())
826              return nil
827            },
828          },
829        },
830      },
831    },
832  }
833
834  err := app.Run(os.Args)
835  if err != nil {
836    log.Fatal(err)
837  }
838}
839```
840
841### Subcommands categories
842
843For additional organization in apps that have many subcommands, you can
844associate a category for each command to group them together in the help
845output.
846
847E.g.
848
849```go
850package main
851
852import (
853  "log"
854  "os"
855
856  "github.com/urfave/cli/v2"
857)
858
859func main() {
860  app := &cli.App{
861    Commands: []*cli.Command{
862      {
863        Name: "noop",
864      },
865      {
866        Name:     "add",
867        Category: "template",
868      },
869      {
870        Name:     "remove",
871        Category: "template",
872      },
873    },
874  }
875
876  err := app.Run(os.Args)
877  if err != nil {
878    log.Fatal(err)
879  }
880}
881```
882
883Will include:
884
885```
886COMMANDS:
887  noop
888
889  Template actions:
890    add
891    remove
892```
893
894### Exit code
895
896Calling `App.Run` will not automatically call `os.Exit`, which means that by
897default the exit code will "fall through" to being `0`.  An explicit exit code
898may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
899`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
900<!-- {
901  "error": "Ginger croutons are not in the soup"
902} -->
903``` go
904package main
905
906import (
907  "log"
908  "os"
909
910  "github.com/urfave/cli/v2"
911)
912
913func main() {
914  app := &cli.App{
915    Flags: []cli.Flag{
916      &cli.BoolFlag{
917        Name:  "ginger-crouton",
918        Usage: "is it in the soup?",
919      },
920    },
921    Action: func(ctx *cli.Context) error {
922      if !ctx.Bool("ginger-crouton") {
923        return cli.Exit("Ginger croutons are not in the soup", 86)
924      }
925      return nil
926    },
927  }
928
929  err := app.Run(os.Args)
930  if err != nil {
931    log.Fatal(err)
932  }
933}
934```
935
936### Combining short options
937
938Traditional use of options using their shortnames look like this:
939
940```
941$ cmd -s -o -m "Some message"
942```
943
944Suppose you want users to be able to combine options with their shortnames. This
945can be done using the `UseShortOptionHandling` bool in your app configuration,
946or for individual commands by attaching it to the command configuration. For
947example:
948
949<!-- {
950  "args": ["short", "&#45;som", "Some message"],
951  "output": "serve: true\noption: true\nmessage: Some message\n"
952} -->
953``` go
954package main
955
956import (
957  "fmt"
958  "log"
959  "os"
960
961  "github.com/urfave/cli/v2"
962)
963
964func main() {
965  app := &cli.App{}
966  app.UseShortOptionHandling = true
967  app.Commands = []*cli.Command{
968    {
969      Name:  "short",
970      Usage: "complete a task on the list",
971      Flags: []cli.Flag{
972        &cli.BoolFlag{Name: "serve", Aliases: []string{"s"}},
973        &cli.BoolFlag{Name: "option", Aliases: []string{"o"}},
974        &cli.StringFlag{Name: "message", Aliases: []string{"m"}},
975      },
976      Action: func(c *cli.Context) error {
977        fmt.Println("serve:", c.Bool("serve"))
978        fmt.Println("option:", c.Bool("option"))
979        fmt.Println("message:", c.String("message"))
980        return nil
981      },
982    },
983  }
984
985  err := app.Run(os.Args)
986  if err != nil {
987    log.Fatal(err)
988  }
989}
990```
991
992If your program has any number of bool flags such as `serve` and `option`, and
993optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
994and `-m` respectively, setting `UseShortOptionHandling` will also support the
995following syntax:
996
997```
998$ cmd -som "Some message"
999```
1000
1001If you enable `UseShortOptionHandling`, then you must not use any flags that
1002have a single leading `-` or this will result in failures. For example,
1003`-option` can no longer be used. Flags with two leading dashes (such as
1004`--options`) are still valid.
1005
1006### Bash Completion
1007
1008You can enable completion commands by setting the `EnableBashCompletion`
1009flag on the `App` object to `true`.  By default, this setting will allow auto-completion
1010for an app's subcommands, but you can write your own completion methods for
1011the App or its subcommands as well.
1012
1013#### Default auto-completion
1014
1015```go
1016package main
1017import (
1018	"fmt"
1019	"log"
1020	"os"
1021	"github.com/urfave/cli/v2"
1022)
1023func main() {
1024	app := cli.NewApp()
1025	app.EnableBashCompletion = true
1026	app.Commands = []*cli.Command{
1027		{
1028			Name:    "add",
1029			Aliases: []string{"a"},
1030			Usage:   "add a task to the list",
1031			Action: func(c *cli.Context) error {
1032				fmt.Println("added task: ", c.Args().First())
1033				return nil
1034			},
1035		},
1036		{
1037			Name:    "complete",
1038			Aliases: []string{"c"},
1039			Usage:   "complete a task on the list",
1040			Action: func(c *cli.Context) error {
1041				fmt.Println("completed task: ", c.Args().First())
1042				return nil
1043			},
1044		},
1045		{
1046			Name:    "template",
1047			Aliases: []string{"t"},
1048			Usage:   "options for task templates",
1049			Subcommands: []*cli.Command{
1050				{
1051					Name:  "add",
1052					Usage: "add a new template",
1053					Action: func(c *cli.Context) error {
1054						fmt.Println("new task template: ", c.Args().First())
1055						return nil
1056					},
1057				},
1058				{
1059					Name:  "remove",
1060					Usage: "remove an existing template",
1061					Action: func(c *cli.Context) error {
1062						fmt.Println("removed task template: ", c.Args().First())
1063						return nil
1064					},
1065				},
1066			},
1067		},
1068	}
1069	err := app.Run(os.Args)
1070	if err != nil {
1071		log.Fatal(err)
1072	}
1073}
1074```
1075![](/docs/v2/images/default-bash-autocomplete.gif)
1076
1077#### Custom auto-completion
1078<!-- {
1079  "args": ["complete", "&#45;&#45;generate&#45;bash&#45;completion"],
1080  "output": "laundry"
1081} -->
1082``` go
1083package main
1084
1085import (
1086  "fmt"
1087  "log"
1088  "os"
1089
1090  "github.com/urfave/cli/v2"
1091)
1092
1093func main() {
1094  tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
1095
1096  app := &cli.App{
1097    EnableBashCompletion: true,
1098    Commands: []*cli.Command{
1099      {
1100        Name:    "complete",
1101        Aliases: []string{"c"},
1102        Usage:   "complete a task on the list",
1103        Action: func(c *cli.Context) error {
1104           fmt.Println("completed task: ", c.Args().First())
1105           return nil
1106        },
1107        BashComplete: func(c *cli.Context) {
1108          // This will complete if no args are passed
1109          if c.NArg() > 0 {
1110            return
1111          }
1112          for _, t := range tasks {
1113            fmt.Println(t)
1114          }
1115        },
1116      },
1117    },
1118  }
1119
1120  err := app.Run(os.Args)
1121  if err != nil {
1122    log.Fatal(err)
1123  }
1124}
1125```
1126![](/docs/v2/images/custom-bash-autocomplete.gif)
1127
1128#### Enabling
1129
1130To enable auto-completion for the current shell session, a bash script,
1131`autocomplete/bash_autocomplete` is included in this repo.
1132
1133To use `autocomplete/bash_autocomplete` set an environment variable named `PROG` to
1134the name of your program and then `source` the `autocomplete/bash_autocomplete` file.
1135
1136For example, if your cli program is called `myprogram`:
1137
1138`PROG=myprogram source path/to/cli/autocomplete/bash_autocomplete`
1139
1140Auto-completion is now enabled for the current shell, but will not persist into a new shell.
1141
1142#### Distribution and Persistent Autocompletion
1143
1144Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
1145it to the name of the program you wish to add autocomplete support for (or
1146automatically install it there if you are distributing a package). Don't forget
1147to source the file or restart your shell to activate the auto-completion.
1148
1149```
1150sudo cp path/to/autocomplete/bash_autocomplete /etc/bash_completion.d/<myprogram>
1151source /etc/bash_completion.d/<myprogram>
1152```
1153
1154Alternatively, you can just document that users should `source` the generic
1155`autocomplete/bash_autocomplete` and set `$PROG` within their bash configuration
1156file, adding these lines:
1157
1158```
1159PROG=<myprogram>
1160source path/to/cli/autocomplete/bash_autocomplete
1161```
1162Keep in mind that if they are enabling auto-completion for more than one program,
1163they will need to set `PROG` and source `autocomplete/bash_autocomplete` for each
1164program, like so:
1165
1166```
1167PROG=<program1>
1168source path/to/cli/autocomplete/bash_autocomplete
1169PROG=<program2>
1170source path/to/cli/autocomplete/bash_autocomplete
1171```
1172
1173#### Customization
1174
1175The default shell completion flag (`--generate-bash-completion`) is defined as
1176`cli.EnableBashCompletion`, and may be redefined if desired, e.g.:
1177
1178<!-- {
1179  "args": ["&#45;&#45;generate&#45;bash&#45;completion"],
1180  "output": "wat\nhelp\nh"
1181} -->
1182``` go
1183package main
1184
1185import (
1186  "log"
1187  "os"
1188
1189  "github.com/urfave/cli/v2"
1190)
1191
1192func main() {
1193  app := &cli.App{
1194    EnableBashCompletion: true,
1195    Commands: []*cli.Command{
1196      {
1197        Name: "wat",
1198      },
1199    },
1200  }
1201  err := app.Run(os.Args)
1202  if err != nil {
1203    log.Fatal(err)
1204  }
1205}
1206```
1207
1208#### ZSH Support
1209Auto-completion for ZSH is also supported using the `autocomplete/zsh_autocomplete`
1210file included in this repo. Two environment variables are used, `PROG` and `_CLI_ZSH_AUTOCOMPLETE_HACK`.
1211Set `PROG` to the program name as before, set `_CLI_ZSH_AUTOCOMPLETE_HACK` to `1`, and
1212then `source path/to/autocomplete/zsh_autocomplete`. Adding the following lines to your ZSH
1213configuration file (usually `.zshrc`) will allow the auto-completion to persist across new shells:
1214
1215```
1216PROG=<myprogram>
1217_CLI_ZSH_AUTOCOMPLETE_HACK=1
1218source  path/to/autocomplete/zsh_autocomplete
1219```
1220#### ZSH default auto-complete example
1221![](/docs/v2/images/default-zsh-autocomplete.gif)
1222#### ZSH custom auto-complete example
1223![](/docs/v2/images/custom-zsh-autocomplete.gif)
1224
1225### Generated Help Text
1226
1227The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
1228by the cli internals in order to print generated help text for the app, command,
1229or subcommand, and break execution.
1230
1231#### Customization
1232
1233All of the help text generation may be customized, and at multiple levels.  The
1234templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
1235`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
1236is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
1237e.g.:
1238
1239<!-- {
1240  "output": "Ha HA.  I pwnd the help!!1"
1241} -->
1242``` go
1243package main
1244
1245import (
1246  "fmt"
1247  "io"
1248  "os"
1249
1250  "github.com/urfave/cli/v2"
1251)
1252
1253func main() {
1254  // EXAMPLE: Append to an existing template
1255  cli.AppHelpTemplate = fmt.Sprintf(`%s
1256
1257WEBSITE: http://awesometown.example.com
1258
1259SUPPORT: support@awesometown.example.com
1260
1261`, cli.AppHelpTemplate)
1262
1263  // EXAMPLE: Override a template
1264  cli.AppHelpTemplate = `NAME:
1265   {{.Name}} - {{.Usage}}
1266USAGE:
1267   {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
1268   {{if len .Authors}}
1269AUTHOR:
1270   {{range .Authors}}{{ . }}{{end}}
1271   {{end}}{{if .Commands}}
1272COMMANDS:
1273{{range .Commands}}{{if not .HideHelp}}   {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
1274GLOBAL OPTIONS:
1275   {{range .VisibleFlags}}{{.}}
1276   {{end}}{{end}}{{if .Copyright }}
1277COPYRIGHT:
1278   {{.Copyright}}
1279   {{end}}{{if .Version}}
1280VERSION:
1281   {{.Version}}
1282   {{end}}
1283`
1284
1285  // EXAMPLE: Replace the `HelpPrinter` func
1286  cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
1287    fmt.Println("Ha HA.  I pwnd the help!!1")
1288  }
1289
1290  (&cli.App{}).Run(os.Args)
1291}
1292```
1293
1294The default flag may be customized to something other than `-h/--help` by
1295setting `cli.HelpFlag`, e.g.:
1296
1297<!-- {
1298  "args": ["&#45;&#45halp"],
1299  "output": "haaaaalp.*HALP"
1300} -->
1301``` go
1302package main
1303
1304import (
1305  "os"
1306
1307  "github.com/urfave/cli/v2"
1308)
1309
1310func main() {
1311  cli.HelpFlag = &cli.BoolFlag{
1312    Name: "haaaaalp", Aliases: []string{"halp"},
1313    Usage: "HALP",
1314    EnvVars: []string{"SHOW_HALP", "HALPPLZ"},
1315  }
1316
1317  (&cli.App{}).Run(os.Args)
1318}
1319```
1320
1321### Version Flag
1322
1323The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
1324is checked by the cli internals in order to print the `App.Version` via
1325`cli.VersionPrinter` and break execution.
1326
1327#### Customization
1328
1329The default flag may be customized to something other than `-v/--version` by
1330setting `cli.VersionFlag`, e.g.:
1331
1332<!-- {
1333  "args": ["&#45;&#45print-version"],
1334  "output": "partay version v19\\.99\\.0"
1335} -->
1336``` go
1337package main
1338
1339import (
1340  "os"
1341
1342  "github.com/urfave/cli/v2"
1343)
1344
1345func main() {
1346  cli.VersionFlag = &cli.BoolFlag{
1347    Name: "print-version", Aliases: []string{"V"},
1348    Usage: "print only the version",
1349  }
1350
1351  app := &cli.App{
1352    Name: "partay",
1353    Version: "v19.99.0",
1354  }
1355  app.Run(os.Args)
1356}
1357```
1358
1359Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
1360
1361<!-- {
1362  "args": ["&#45;&#45version"],
1363  "output": "version=v19\\.99\\.0 revision=fafafaf"
1364} -->
1365``` go
1366package main
1367
1368import (
1369  "fmt"
1370  "os"
1371
1372  "github.com/urfave/cli/v2"
1373)
1374
1375var (
1376  Revision = "fafafaf"
1377)
1378
1379func main() {
1380  cli.VersionPrinter = func(c *cli.Context) {
1381    fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
1382  }
1383
1384  app := &cli.App{
1385    Name: "partay",
1386    Version: "v19.99.0",
1387  }
1388  app.Run(os.Args)
1389}
1390```
1391
1392### Timestamp Flag
1393
1394Using the timestamp flag is simple. Please refer to [`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible formats.
1395
1396<!-- {
1397  "args": ["&#45;&#45;meeting", "2019-08-12T15:04:05"],
1398  "output": "2019\\-08\\-12 15\\:04\\:05 \\+0000 UTC"
1399} -->
1400``` go
1401package main
1402
1403import (
1404  "fmt"
1405  "log"
1406  "os"
1407
1408  "github.com/urfave/cli/v2"
1409)
1410
1411func main() {
1412  app := &cli.App{
1413    Flags: []cli.Flag {
1414      &cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05"},
1415    },
1416    Action: func(c *cli.Context) error {
1417      fmt.Printf("%s", c.Timestamp("meeting").String())
1418      return nil
1419    },
1420  }
1421
1422  err := app.Run(os.Args)
1423  if err != nil {
1424    log.Fatal(err)
1425  }
1426}
1427```
1428
1429In this example the flag could be used like this :
1430
1431`myapp --meeting 2019-08-12T15:04:05`
1432
1433Side note: quotes may be necessary around the date depending on your layout (if you have spaces for instance)
1434
1435### Full API Example
1436
1437**Notice**: This is a contrived (functioning) example meant strictly for API
1438demonstration purposes.  Use of one's imagination is encouraged.
1439
1440<!-- {
1441  "output": "made it!\nPhew!"
1442} -->
1443``` go
1444package main
1445
1446import (
1447  "errors"
1448  "flag"
1449  "fmt"
1450  "io"
1451  "io/ioutil"
1452  "os"
1453  "time"
1454
1455  "github.com/urfave/cli/v2"
1456)
1457
1458func init() {
1459  cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
1460  cli.CommandHelpTemplate += "\nYMMV\n"
1461  cli.SubcommandHelpTemplate += "\nor something\n"
1462
1463  cli.HelpFlag = &cli.BoolFlag{Name: "halp"}
1464  cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}}
1465
1466  cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) {
1467    fmt.Fprintf(w, "best of luck to you\n")
1468  }
1469  cli.VersionPrinter = func(c *cli.Context) {
1470    fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version)
1471  }
1472  cli.OsExiter = func(c int) {
1473    fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c)
1474  }
1475  cli.ErrWriter = ioutil.Discard
1476  cli.FlagStringer = func(fl cli.Flag) string {
1477    return fmt.Sprintf("\t\t%s", fl.Names()[0])
1478  }
1479}
1480
1481type hexWriter struct{}
1482
1483func (w *hexWriter) Write(p []byte) (int, error) {
1484  for _, b := range p {
1485    fmt.Printf("%x", b)
1486  }
1487  fmt.Printf("\n")
1488
1489  return len(p), nil
1490}
1491
1492type genericType struct {
1493  s string
1494}
1495
1496func (g *genericType) Set(value string) error {
1497  g.s = value
1498  return nil
1499}
1500
1501func (g *genericType) String() string {
1502  return g.s
1503}
1504
1505func main() {
1506  app := &cli.App{
1507    Name: "kənˈtrīv",
1508    Version: "v19.99.0",
1509    Compiled: time.Now(),
1510    Authors: []*cli.Author{
1511      &cli.Author{
1512        Name:  "Example Human",
1513        Email: "human@example.com",
1514      },
1515    },
1516    Copyright: "(c) 1999 Serious Enterprise",
1517    HelpName: "contrive",
1518    Usage: "demonstrate available API",
1519    UsageText: "contrive - demonstrating the available API",
1520    ArgsUsage: "[args and such]",
1521    Commands: []*cli.Command{
1522      &cli.Command{
1523        Name:        "doo",
1524        Aliases:     []string{"do"},
1525        Category:    "motion",
1526        Usage:       "do the doo",
1527        UsageText:   "doo - does the dooing",
1528        Description: "no really, there is a lot of dooing to be done",
1529        ArgsUsage:   "[arrgh]",
1530        Flags: []cli.Flag{
1531          &cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}},
1532        },
1533        Subcommands: []*cli.Command{
1534          &cli.Command{
1535            Name:   "wop",
1536            Action: wopAction,
1537          },
1538        },
1539        SkipFlagParsing: false,
1540        HideHelp:        false,
1541        Hidden:          false,
1542        HelpName:        "doo!",
1543        BashComplete: func(c *cli.Context) {
1544          fmt.Fprintf(c.App.Writer, "--better\n")
1545        },
1546        Before: func(c *cli.Context) error {
1547          fmt.Fprintf(c.App.Writer, "brace for impact\n")
1548          return nil
1549        },
1550        After: func(c *cli.Context) error {
1551          fmt.Fprintf(c.App.Writer, "did we lose anyone?\n")
1552          return nil
1553        },
1554        Action: func(c *cli.Context) error {
1555          c.Command.FullName()
1556          c.Command.HasName("wop")
1557          c.Command.Names()
1558          c.Command.VisibleFlags()
1559          fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n")
1560          if c.Bool("forever") {
1561            c.Command.Run(c)
1562          }
1563          return nil
1564        },
1565        OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
1566          fmt.Fprintf(c.App.Writer, "for shame\n")
1567          return err
1568        },
1569      },
1570    },
1571    Flags: []cli.Flag{
1572      &cli.BoolFlag{Name: "fancy"},
1573      &cli.BoolFlag{Value: true, Name: "fancier"},
1574      &cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3},
1575      &cli.Float64Flag{Name: "howmuch"},
1576      &cli.GenericFlag{Name: "wat", Value: &genericType{}},
1577      &cli.Int64Flag{Name: "longdistance"},
1578      &cli.Int64SliceFlag{Name: "intervals"},
1579      &cli.IntFlag{Name: "distance"},
1580      &cli.IntSliceFlag{Name: "times"},
1581      &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}},
1582      &cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}},
1583      &cli.UintFlag{Name: "age"},
1584      &cli.Uint64Flag{Name: "bigage"},
1585    },
1586    EnableBashCompletion: true,
1587    HideHelp: false,
1588    HideVersion: false,
1589    BashComplete: func(c *cli.Context) {
1590      fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
1591    },
1592    Before: func(c *cli.Context) error {
1593      fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
1594      return nil
1595    },
1596    After: func(c *cli.Context) error {
1597      fmt.Fprintf(c.App.Writer, "Phew!\n")
1598      return nil
1599    },
1600    CommandNotFound: func(c *cli.Context, command string) {
1601      fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
1602    },
1603    OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error {
1604      if isSubcommand {
1605        return err
1606      }
1607
1608      fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
1609      return nil
1610    },
1611    Action: func(c *cli.Context) error {
1612      cli.DefaultAppComplete(c)
1613      cli.HandleExitCoder(errors.New("not an exit coder, though"))
1614      cli.ShowAppHelp(c)
1615      cli.ShowCommandCompletions(c, "nope")
1616      cli.ShowCommandHelp(c, "also-nope")
1617      cli.ShowCompletions(c)
1618      cli.ShowSubcommandHelp(c)
1619      cli.ShowVersion(c)
1620
1621      fmt.Printf("%#v\n", c.App.Command("doo"))
1622      if c.Bool("infinite") {
1623      	c.App.Run([]string{"app", "doo", "wop"})
1624      }
1625
1626      if c.Bool("forevar") {
1627      	c.App.RunAsSubcommand(c)
1628      }
1629      c.App.Setup()
1630      fmt.Printf("%#v\n", c.App.VisibleCategories())
1631      fmt.Printf("%#v\n", c.App.VisibleCommands())
1632      fmt.Printf("%#v\n", c.App.VisibleFlags())
1633
1634      fmt.Printf("%#v\n", c.Args().First())
1635      if c.Args().Len() > 0 {
1636        fmt.Printf("%#v\n", c.Args().Get(1))
1637      }
1638      fmt.Printf("%#v\n", c.Args().Present())
1639      fmt.Printf("%#v\n", c.Args().Tail())
1640
1641      set := flag.NewFlagSet("contrive", 0)
1642      nc := cli.NewContext(c.App, set, c)
1643
1644      fmt.Printf("%#v\n", nc.Args())
1645      fmt.Printf("%#v\n", nc.Bool("nope"))
1646      fmt.Printf("%#v\n", !nc.Bool("nerp"))
1647      fmt.Printf("%#v\n", nc.Duration("howlong"))
1648      fmt.Printf("%#v\n", nc.Float64("hay"))
1649      fmt.Printf("%#v\n", nc.Generic("bloop"))
1650      fmt.Printf("%#v\n", nc.Int64("bonk"))
1651      fmt.Printf("%#v\n", nc.Int64Slice("burnks"))
1652      fmt.Printf("%#v\n", nc.Int("bips"))
1653      fmt.Printf("%#v\n", nc.IntSlice("blups"))
1654      fmt.Printf("%#v\n", nc.String("snurt"))
1655      fmt.Printf("%#v\n", nc.StringSlice("snurkles"))
1656      fmt.Printf("%#v\n", nc.Uint("flub"))
1657      fmt.Printf("%#v\n", nc.Uint64("florb"))
1658
1659      fmt.Printf("%#v\n", nc.FlagNames())
1660      fmt.Printf("%#v\n", nc.IsSet("wat"))
1661      fmt.Printf("%#v\n", nc.Set("wat", "nope"))
1662      fmt.Printf("%#v\n", nc.NArg())
1663      fmt.Printf("%#v\n", nc.NumFlags())
1664      fmt.Printf("%#v\n", nc.Lineage()[1])
1665      nc.Set("wat", "also-nope")
1666
1667      ec := cli.Exit("ohwell", 86)
1668      fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
1669      fmt.Printf("made it!\n")
1670      return ec
1671    },
1672    Metadata: map[string]interface{}{
1673      "layers":          "many",
1674      "explicable":      false,
1675      "whatever-values": 19.99,
1676    },
1677  }
1678
1679  if os.Getenv("HEXY") != "" {
1680    app.Writer = &hexWriter{}
1681    app.ErrWriter = &hexWriter{}
1682  }
1683
1684  app.Run(os.Args)
1685}
1686
1687func wopAction(c *cli.Context) error {
1688  fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
1689  return nil
1690}
1691```
1692