{"id":13410312,"url":"https://github.com/mkideal/cli","last_synced_at":"2026-01-16T19:50:02.780Z","repository":{"id":4282957,"uuid":"52618352","full_name":"mkideal/cli","owner":"mkideal","description":"CLI - A package for building command line app with go","archived":false,"fork":false,"pushed_at":"2024-02-04T08:43:26.000Z","size":5146,"stargazers_count":727,"open_issues_count":4,"forks_count":43,"subscribers_count":23,"default_branch":"master","last_synced_at":"2024-07-31T20:42:14.535Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mkideal.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-02-26T16:45:29.000Z","updated_at":"2024-07-25T16:44:29.000Z","dependencies_parsed_at":"2022-07-29T19:19:21.068Z","dependency_job_id":"f217e840-c6e0-4f9c-b610-14827e6d4f22","html_url":"https://github.com/mkideal/cli","commit_stats":{"total_commits":228,"total_committers":15,"mean_commits":15.2,"dds":0.3070175438596491,"last_synced_commit":"53df0924b380e11eb521fb80aab3343adbf7f70d"},"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/mkideal/cli","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkideal%2Fcli","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkideal%2Fcli/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkideal%2Fcli/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkideal%2Fcli/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mkideal","download_url":"https://codeload.github.com/mkideal/cli/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mkideal%2Fcli/sbom","scorecard":{"id":652515,"data":{"date":"2025-08-11","repo":{"name":"github.com/mkideal/cli","commit":"53df0924b380e11eb521fb80aab3343adbf7f70d"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":2,"reason":"Found 5/19 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 20 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":7,"reason":"3 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GO-2023-2402 / GHSA-45x7-px36-x8w8","Warn: Project is vulnerable to: GO-2024-3321 / GHSA-v778-237x-gjrc","Warn: Project is vulnerable to: GO-2025-3487 / GHSA-hcg3-q754-cr77"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-21T13:50:15.506Z","repository_id":4282957,"created_at":"2025-08-21T13:50:15.506Z","updated_at":"2025-08-21T13:50:15.506Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28481993,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-30T20:01:06.149Z","updated_at":"2026-01-16T19:50:02.758Z","avatar_url":"https://github.com/mkideal.png","language":"Go","readme":"Command line interface\n======================\n\n[![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://raw.githubusercontent.com/mkideal/cli/master/LICENSE) [![Travis branch](https://img.shields.io/travis/mkideal/cli/master.svg)](https://travis-ci.org/mkideal/cli) [![Coverage Status](https://coveralls.io/repos/github/mkideal/cli/badge.svg?branch=master)](https://coveralls.io/github/mkideal/cli?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/mkideal/cli)](https://goreportcard.com/report/github.com/mkideal/cli) [![GoDoc](https://godoc.org/github.com/mkideal/cli?status.svg)](https://godoc.org/github.com/mkideal/cli)\n\nScreenshot\n----------\n\n![screenshot2](http://www.mkideal.com/assets/images/cli/screenshot.png)\n\nKey features\n------------\n\n-\tLightweight and easy to use.\n-\tDefines flag by tag, e.g. flag name(short or/and long), description, default value, password, prompt and so on.\n-\tType safety.\n-\tOutput looks very nice.\n-\tSupports custom Validator.\n-\tSupports slice and map as a flag.\n-\tSupports any type as a flag field which implements cli.Decoder interface.\n-\tSupports any type as a flag field which uses FlagParser.\n-\tSuggestions for command.(e.g. `hl` =\u003e `help`, \"veron\" =\u003e \"version\").\n-\tSupports default value for flag, even expression about env variable(e.g. `dft:\"$HOME/dev\"`).\n-\tSupports editor like `git commit` command.(See example [21](http://www.mkideal.com/golang/cli-examples.html#example-21-editor) and [22](http://www.mkideal.com/golang/cli-examples.html#example-22-custom-editor)\\)\n\nAPI documentation\n-----------------\n\nSee [**godoc**](https://godoc.org/github.com/mkideal/cli)\n\nExamples\n--------\n\n-\t[Example 1: Hello world](#example-1-hello)\n-\t[Example 2: How to use **flag**](#example-2-flag)\n-\t[Example 3: How to use **required** flag](#example-3-required-flag)\n-\t[Example 4: How to use **default** flag](#example-4-default-flag)\n-\t[Example 5: How to use **slice**](#example-5-slice)\n-\t[Example 6: How to use **map**](#example-6-map)\n-\t[Example 7: Usage of **force** flag](#example-7-force-flag)\n-\t[Example 8: Usage of **child command**](#example-8-child-command)\n-\t[Example 9: **Auto help**](#example-9-auto-help)\n-\t[Example 10: Usage of **Validator**](#example-10-usage-of-validator)\n-\t[Example 11: **Prompt** and **Password**](#example-11-prompt-and-password)\n-\t[Example 12: How to use **Decoder**](#example-12-decoder)\n-\t[Example 13: Builtin Decoder: **PidFile**](#example-13-pid-file)\n-\t[Example 14: Builtin Decoder: **Time** and **Duration**](#example-14-time-and-duration)\n-\t[Example 15: Builtin Decoder: **File**](#example-15-file)\n-\t[Example 16: **Parser**](#example-16-parser)\n-\t[Example 17: Builtin Parser: **JSONFileParser**](#example-17-json-file)\n-\t[Example 18: How to use **custom parser**](#example-18-custom-parser)\n-\t[Example 19: How to use **Hooks**](#example-19-hooks)\n-\t[Example 20: How to use **Daemon**](#example-20-daemon)\n-\t[Example 21: How to use **Editor**](#example-21-editor)\n-\t[Example 22: Custom **Editor**](#example-22-custom-editor)\n-\t[Example 23: How to hide/disable/deprecate **flag**](#example-23-hide-flag)\n\n### Example 1: Hello\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This is a HelloWorld-like example\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tName string `cli:\"name\" usage:\"tell me your name\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"Hello, %s!\\n\", argv.Name)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o hello\n$ ./hello --name Clipher\nHello, Clipher!\n```\n\n### Example 2: Flag\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show basic usage of flag\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tPort int  `cli:\"p,port\" usage:\"short and long format flags both are supported\"`\n\tX    bool `cli:\"x\" usage:\"boolean type\"`\n\tY    bool `cli:\"y\" usage:\"boolean type, too\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"port=%d, x=%v, y=%v\\n\", argv.Port, argv.X, argv.Y)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -h\nOptions:\n\n  -h, --help     display help information\n  -p, --port     short and long format flags both are supported\n  -x             boolean type\n  -y             boolean type, too\n$ ./app -p=8080 -x\nport=8080, x=true, y=false\n$ ./app -p 8080 -x=true\nport=8080, x=true, y=false\n$ ./app -p8080 -y true\nport=8080, x=false, y=true\n$ ./app --port=8080 -xy\nport=8080, x=true, y=true\n$ ./app --port 8080 -yx\nport=8080, x=true, y=true\n```\n\n### Example 3: Required flag\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use required flag\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tId uint8 `cli:\"*id\" usage:\"this is a required flag, note the *\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"%d\\n\", argv.Id)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\nERR! required argument --id missing\n$ ./app --id=2\n2\n```\n\n### Example 4: Default flag\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use default flag\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tBasic  int    `cli:\"basic\" usage:\"basic usage of default\" dft:\"2\"`\n\tEnv    string `cli:\"env\" usage:\"env variable as default\" dft:\"$HOME\"`\n\tExpr   int    `cli:\"expr\" usage:\"expression as default\" dft:\"$BASE_PORT+1000\"`\n\tDevDir string `cli:\"devdir\" usage:\"directory of developer\" dft:\"$HOME/dev\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"%d, %s, %d, %s\\n\", argv.Basic, argv.Env, argv.Expr, argv.DevDir)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -h\nOptions:\n\n  -h, --help                       display help information\n      --basic[=2]                  basic usage of default\n      --env[=$HOME]                env variable as default\n      --expr[=$BASE_PORT+1000]     expression as default\n      --devdir[=$HOME/dev]         directory of developer\n$ ./app\n2, /Users/wang, 1000, /Users/wang/dev\n$ BASE_PORT=8000 ./app --basic=3\n3, /Users/wang, 9000, /Users/wang/dev\n```\n\n### Example 5: Slice\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use slice as a flag\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\t// []bool, []int, []float32, ... supported too.\n\tFriends []string `cli:\"F\" usage:\"my friends\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\tctx.JSONln(ctx.Argv())\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\n{\"Friends\":null}\n$ ./app -FAlice -FBob -F Charlie\n{\"Friends\":[\"Alice\",\"Bob\",\"Charlie\"]}\n```\n\n### Example 6: Map\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use map as a flag\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tMacros map[string]int `cli:\"D\" usage:\"define macros\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\tctx.JSONln(ctx.Argv())\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\n{\"Macros\":null}\n$ ./app -Dx=not-a-number\nERR! `not-a-number` couldn't converted to an int value\n$ ./app -Dx=1 -D y=2\n{\"Macros\":{\"x\":1,\"y\":2}}\n```\n\n### Example 7: Force flag\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show usage of force flag\n// Force flag has prefix !, and must be a boolean.\n// Will prevent validating flags if some force flag assigned true\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tVersion  bool `cli:\"!v\" usage:\"force flag, note the !\"`\n\tRequired int  `cli:\"*r\" usage:\"required flag\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tif argv.Version {\n\t\t\tctx.String(\"v0.0.1\\n\")\n\t\t}\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\nERR! required argument -r missing\n\n# -v is a force flag, and assigned true, so `ERR` disappear.\n$ ./app -v\nv0.0.1\n```\n\n### Example 8: Child command\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates usage of child command\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\nfunc main() {\n\tif err := cli.Root(root,\n\t\tcli.Tree(help),\n\t\tcli.Tree(child),\n\t).Run(os.Args[1:]); err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n}\n\nvar help = cli.HelpCommand(\"display help information\")\n\n// root command\ntype rootT struct {\n\tcli.Helper\n\tName string `cli:\"name\" usage:\"your name\"`\n}\n\nvar root = \u0026cli.Command{\n\tDesc: \"this is root command\",\n\t// Argv is a factory function of argument object\n\t// ctx.Argv() is if Command.Argv == nil or Command.Argv() is nil\n\tArgv: func() interface{} { return new(rootT) },\n\tFn: func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*rootT)\n\t\tctx.String(\"Hello, root command, I am %s\\n\", argv.Name)\n\t\treturn nil\n\t},\n}\n\n// child command\ntype childT struct {\n\tcli.Helper\n\tName string `cli:\"name\" usage:\"your name\"`\n}\n\nvar child = \u0026cli.Command{\n\tName: \"child\",\n\tDesc: \"this is a child command\",\n\tArgv: func() interface{} { return new(childT) },\n\tFn: func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*childT)\n\t\tctx.String(\"Hello, child command, I am %s\\n\", argv.Name)\n\t\treturn nil\n\t},\n}\n```\n\n```sh\n$ go build -o app\n\n# help for root\n# equivalent to \"./app -h\"\n$ ./app help\nthis is root command\n\nOptions:\n\n  -h, --help     display help information\n      --name     your name\n\nCommands:\n  help    display help information\n  child   this is a child command\n\n# help for specific command\n# equivalent to \"./app child -h\"\n$ ./app help child\nthis is a child command\n\nOptions:\n\n  -h, --help     display help information\n      --name     your name\n\n# execute root command\n$ ./app --name 123\nHello, root command, I am 123\n\n# execute child command\n$ ./app child --name=123\nHello, child command, I am 123\n\n# something wrong, but got a suggestion.\n$ ./app chd\nERR! command chd not found\nDid you mean child?\n```\n\n### Example 9: Auto help\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates cli.AutoHelper\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tHelp bool `cli:\"h,help\" usage:\"show help\"`\n}\n\n// AutoHelp implements cli.AutoHelper interface\n// NOTE: cli.Helper is a predefined type which implements cli.AutoHelper\nfunc (argv *argT) AutoHelp() bool {\n\treturn argv.Help\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -h\nOptions:\n\n  -h, --help     show help\n```\n\nTry comment `AutoHelp` method and rerun it.\n\n### Example 10: Usage of Validator\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates how to utilize Validator\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tAge    int    `cli:\"age\" usage:\"your age\"`\n\tGender string `cli:\"g,gender\" usage:\"your gender\" dft:\"male\"`\n}\n\n// Validate implements cli.Validator interface\nfunc (argv *argT) Validate(ctx *cli.Context) error {\n\tif argv.Age \u003c 0 || argv.Age \u003e 300 {\n\t\treturn fmt.Errorf(\"age %d out of range\", argv.Age)\n\t}\n\tif argv.Gender != \"male\" \u0026\u0026 argv.Gender != \"female\" {\n\t\treturn fmt.Errorf(\"invalid gender %s\", ctx.Color().Yellow(argv.Gender))\n\t}\n\treturn nil\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\tctx.JSONln(ctx.Argv())\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app --age=-1\nERR! age -1 out of range\n$ ./app --age=1000\nERR! age 1000 out of range\n$ ./app -g balabala\nERR! invalid gender balabala\n$ ./app --age 88 --gender female\n{\"Help\":false,\"Age\":88,\"Gender\":\"female\"}\n```\n\n### Example 11: Prompt and Password\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example introduce prompt and pw tag\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tUsername string `cli:\"u,username\" usage:\"github account\" prompt:\"type github account\"`\n\tPassword string `pw:\"p,password\" usage:\"password of github account\" prompt:\"type the password\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"username=%s, password=%s\\n\", argv.Username, argv.Password)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\ntype github account: hahaha # visible\ntype the password: # invisible because of `pw` tag\nusername=hahaha, password=123456\n```\n\n### Example 12: Decoder\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use decoder\n\npackage main\n\nimport (\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype exampleDecoder struct {\n\tlist []string\n}\n\n// Decode implements cli.Decoder interface\nfunc (d *exampleDecoder) Decode(s string) error {\n\td.list = strings.Split(s, \",\")\n\treturn nil\n}\n\ntype argT struct {\n\tExample exampleDecoder `cli:\"d\" usage:\"example decoder\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.JSONln(argv.Example.list)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -d a,b,c\n[\"a\",\"b\",\"c\"]\n```\n\n### Example 13: Pid file\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use builtin Decoder: PidFile\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n\tclix \"github.com/mkideal/cli/ext\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tPidFile clix.PidFile `cli:\"pid\" usage:\"pid file\" dft:\"013-pidfile.pid\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\n\t\tif err := argv.PidFile.New(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer argv.PidFile.Remove()\n\n\t\treturn nil\n\t}))\n}\n```\n\n### Example 14: Time and Duration\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use builtin Decoder: Time and Duration\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n\tclix \"github.com/mkideal/cli/ext\"\n)\n\ntype argT struct {\n\tTime     clix.Time     `cli:\"t\" usage:\"time\"`\n\tDuration clix.Duration `cli:\"d\" usage:\"duration\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"time=%v, duration=%v\\n\", argv.Time, argv.Duration)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -t '2016-1-2 3:5' -d=10ms\ntime=2016-01-02 03:05:00 +0800 CST, duration=10ms\n```\n\n### Example 15: File\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use builtin Decoder: File\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n\tclix \"github.com/mkideal/cli/ext\"\n)\n\ntype argT struct {\n\tContent clix.File `cli:\"f,file\" usage:\"read content from file or stdin\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(argv.Content.String())\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n# read from stdin\n$ echo hello | ./app -f\nhello\n# read from file\n$ echo hello \u003e test.txt \u0026\u0026 ./app -f test.txt\nhello\n$ rm test.txt\n```\n\n### Example 16: Parser\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example introduce Parser\n// `Parser` is another way to use custom type of data.\n// Unlike `Decoder`, `Parser` used to parse string according to specific rule,\n// like json,yaml and so on.\n//\n// Builtin parsers:\n// * json\n// * jsonfile\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype config struct {\n\tA string\n\tB int\n\tC bool\n}\n\ntype argT struct {\n\tJSON config `cli:\"c,config\" usage:\"parse json string\" parser:\"json\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.JSONIndentln(argv.JSON, \"\", \"    \")\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\n{\n    \"A\": \"\",\n    \"B\": 0,\n    \"C\": false\n}\n$ ./app -c '{\"A\": \"hello\", \"b\": 22, \"C\": true}'\n{\n    \"A\": \"hello\",\n    \"B\": 22,\n    \"C\": true\n}\n```\n\n### Example 17: JSON file\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example show how to use builtin parser: jsonfile\n// It's similar to json, but read string from file.\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype config struct {\n\tA string\n\tB int\n\tC bool\n}\n\ntype argT struct {\n\tJSON config `cli:\"c,config\" usage:\"parse json from file\" parser:\"jsonfile\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.JSONIndentln(argv.JSON, \"\", \"    \")\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ echo '{\"A\": \"hello\", \"b\": 22, \"C\": true}' \u003e test.json\n$ ./app -c test.json\n{\n    \"A\": \"hello\",\n    \"B\": 22,\n    \"C\": true\n}\n$ rm test.json\n```\n\n### Example 18: Custom parser\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates how to use custom parser\n\npackage main\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype myParser struct {\n\tptr interface{}\n}\n\nfunc newMyParser(ptr interface{}) cli.FlagParser {\n\treturn \u0026myParser{ptr}\n}\n\n// Parse implements FlagParser.Parse interface\nfunc (parser *myParser) Parse(s string) error {\n\ttyp := reflect.TypeOf(parser.ptr)\n\tval := reflect.ValueOf(parser.ptr)\n\tif typ.Kind() == reflect.Ptr {\n\t\tkind := reflect.Indirect(val).Type().Kind()\n\t\tif kind == reflect.Struct {\n\t\t\ttypElem, valElem := typ.Elem(), val.Elem()\n\t\t\tnumField := valElem.NumField()\n\t\t\tfor i := 0; i \u003c numField; i++ {\n\t\t\t\t_, valField := typElem.Field(i), valElem.Field(i)\n\t\t\t\tif valField.Kind() == reflect.Int \u0026\u0026\n\t\t\t\t\tvalField.CanSet() {\n\t\t\t\t\tvalField.SetInt(2)\n\t\t\t\t}\n\t\t\t\tif valField.Kind() == reflect.String \u0026\u0026\n\t\t\t\t\tvalField.CanSet() {\n\t\t\t\t\tvalField.SetString(\"B\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\ntype config struct {\n\tA int\n\tB string\n}\n\ntype argT struct {\n\tCfg config `cli:\"cfg\" parser:\"myparser\"`\n}\n\nfunc main() {\n\t// register parser factory function\n\tcli.RegisterFlagParser(\"myparser\", newMyParser)\n\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"%v\\n\", argv.Cfg)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app\n{0 }\n$ ./app --cfg xxx\n{2 B}\n```\n\n### Example 19: Hooks\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates how to use hooks\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\nfunc main() {\n\tif err := cli.Root(root,\n\t\tcli.Tree(child1),\n\t\tcli.Tree(child2),\n\t).Run(os.Args[1:]); err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n}\n\ntype argT struct {\n\tError bool `cli:\"e\" usage:\"return error\"`\n}\n\nvar root = \u0026cli.Command{\n\tName: \"app\",\n\tArgv: func() interface{} { return new(argT) },\n\tOnRootBefore: func(ctx *cli.Context) error {\n\t\tctx.String(\"OnRootBefore invoked\\n\")\n\t\treturn nil\n\t},\n\tOnRootAfter: func(ctx *cli.Context) error {\n\t\tctx.String(\"OnRootAfter invoked\\n\")\n\t\treturn nil\n\t},\n\tFn: func(ctx *cli.Context) error {\n\t\tctx.String(\"exec root command\\n\")\n\t\targv := ctx.Argv().(*argT)\n\t\tif argv.Error {\n\t\t\treturn fmt.Errorf(\"root command returns error\")\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar child1 = \u0026cli.Command{\n\tName: \"child1\",\n\tArgv: func() interface{} { return new(argT) },\n\tOnBefore: func(ctx *cli.Context) error {\n\t\tctx.String(\"child1's OnBefore invoked\\n\")\n\t\treturn nil\n\t},\n\tOnAfter: func(ctx *cli.Context) error {\n\t\tctx.String(\"child1's OnAfter invoked\\n\")\n\t\treturn nil\n\t},\n\tFn: func(ctx *cli.Context) error {\n\t\tctx.String(\"exec child1 command\\n\")\n\t\targv := ctx.Argv().(*argT)\n\t\tif argv.Error {\n\t\t\treturn fmt.Errorf(\"child1 command returns error\")\n\t\t}\n\t\treturn nil\n\t},\n}\n\nvar child2 = \u0026cli.Command{\n\tName:   \"child2\",\n\tNoHook: true,\n\tFn: func(ctx *cli.Context) error {\n\t\tctx.String(\"exec child2 command\\n\")\n\t\treturn nil\n\t},\n}\n```\n\n```sh\n$ go build -o app\n# OnRootBefore =\u003e Fn =\u003e OnRootAfter\n$ ./app\nOnRootBefore invoked\nexec root command\nOnRootAfter invoked\n# OnBefore =\u003e OnRootBefore =\u003e Fn =\u003e OnRootAfter =\u003e OnAfter\n$ ./app child1\nchild1 OnBefore invoked\nOnRootBefore invoked\nexec child1 command\nOnRootAfter invoked\nchild1 OnAfter invoked\n# No hooks\n$ ./app child2\nexec child2 command\n# OnRootBefore =\u003e Fn --\u003e Error\n$ ./app -e\nOnRootBefore invoked\nexec root command\nroot command returns error\n# OnBefore =\u003e OnRootBefore =\u003e Fn --\u003e Error\n$ ./app child1 -e\nchild1 OnBefore invoked\nOnRootBefore invoked\nexec child1 command\nchild1 command returns error\n```\n\n### Example 20: Daemon\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates how to use `Daemon`\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tWait  uint `cli:\"wait\" usage:\"seconds for waiting\" dft:\"10\"`\n\tError bool `cli:\"e\" usage:\"create an error\"`\n}\n\nconst successResponsePrefix = \"start ok\"\n\nfunc main() {\n\tif err := cli.Root(root,\n\t\tcli.Tree(daemon),\n\t).Run(os.Args[1:]); err != nil {\n\t\tfmt.Fprintln(os.Stderr, err)\n\t\tos.Exit(1)\n\t}\n}\n\nvar root = \u0026cli.Command{\n\tArgv: func() interface{} { return new(argT) },\n\tFn: func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tif argv.Error {\n\t\t\terr := fmt.Errorf(\"occurs error\")\n\t\t\tcli.DaemonResponse(err.Error())\n\t\t\treturn err\n\t\t}\n\t\tcli.DaemonResponse(successResponsePrefix)\n\t\t\u003c-time.After(time.Duration(argv.Wait) * time.Second)\n\t\treturn nil\n\t},\n}\n\nvar daemon = \u0026cli.Command{\n\tName: \"daemon\",\n\tArgv: func() interface{} { return new(argT) },\n\tFn: func(ctx *cli.Context) error {\n\t\treturn cli.Daemon(ctx, successResponsePrefix)\n\t},\n}\n```\n\n```sh\n$ go build -o daemon-app\n$ ./daemone-app daemon\nstart ok\n# Within 10 seconds, you will see process \"./daemon-app\"\n$ ps | grep daemon-app\n11913 ttys002    0:00.01 ./daemon-app\n11915 ttys002    0:00.00 grep daemon-app\n# After 10 seconds\n$ ps | grep daemon-app\n11936 ttys002    0:00.00 grep daemon-app\n# try again with an error\n$ ./daemon-app daemon -e\noccurs error\n$ ps | grep daemon-app\n11936 ttys002    0:00.00 grep daemon-app\n```\n\n### Example 21: Editor\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates how to use `editor`. This similar to git commit\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tMsg string `edit:\"m\" usage:\"message\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"msg: %s\", argv.Msg)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -m \"hello, editor\"\nmsg: hello, editor\n$ ./app # Then, launch a editor(default is vim) and type `hello, editor`, quit the editor\nmsg: hello, editor\n```\n\n### Example 22: Custom Editor\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example demonstrates specific editor.\n\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype argT struct {\n\tcli.Helper\n\tMsg string `edit:\"m\" usage:\"message\"`\n}\n\nfunc main() {\n\tcli.GetEditor = func() (string, error) {\n\t\tif editor := os.Getenv(\"EDITOR\"); editor != \"\" {\n\t\t\treturn editor, nil\n\t\t}\n\t\treturn cli.DefaultEditor, nil\n\t}\n\tos.Exit(cli.Run(new(argT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*argT)\n\t\tctx.String(\"msg: %s\", argv.Msg)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -m \"hello, editor\"\nmsg: hello, editor\n$ EDITOR=nano ./app # Then, launch nano and type `hello, editor`, quit the editor\nmsg: hello, editor\n```\n\n### Example 23: Hide flag\n\n[back to **examples**](#examples)\n\n```go\n// main.go\n// This example hides Gender and InternalUsage flags.\npackage main\n\nimport (\n\t\"os\"\n\n\t\"github.com/mkideal/cli\"\n)\n\ntype helloT struct {\n\tcli.Helper\n\tName          string `cli:\"name\" usage:\"tell me your name\" dft:\"world\"`\n\tGender        string `cli:\"-\"` // deprecated\n\tInternalUsage string `cli:\"-\"` // hide\n\tAge           uint8  `cli:\"a,age\" usage:\"tell me your age\" dft:\"100\"`\n}\n\nfunc main() {\n\tos.Exit(cli.Run(new(helloT), func(ctx *cli.Context) error {\n\t\targv := ctx.Argv().(*helloT)\n\t\tctx.String(\"Hello, %s! Your age is %d?\\n\", argv.Name, argv.Age)\n\t\treturn nil\n\t}))\n}\n```\n\n```sh\n$ go build -o app\n$ ./app -h\nOptions:\n\n  -h, --help           display help information\n      --name[=world]   tell me your name\n  -a, --age[=100]      tell me your age\n```\n","funding_links":[],"categories":["Command Line","Go","CLI frameworks","命令行","others","命令行工具### 标准 CLI`用于创建一个标准命令行应用程序的库`","命令行工具","Build Automation","\u003cspan id=\"命令行-command-line\"\u003e命令行 Command Line\u003c/span\u003e"],"sub_categories":["Standard CLI","标准命令行交互","標準命令行交互","标准CLI","标准 CLI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkideal%2Fcli","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmkideal%2Fcli","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmkideal%2Fcli/lists"}