├── tests ├── VERSION ├── TODO.md ├── test_content.txt ├── exit_handler.sh ├── expect_build_doc.txt ├── input_for_build_doc.txt ├── get_doctops.bats ├── functional_tests_docopts.bats ├── build_doc.bats ├── docopts-auto.bats └── docopts.bats ├── VERSION ├── Changelog.md ├── .gitignore ├── ci ├── get_bats-core.sh ├── get_bash5_macos.sh ├── cp_to_travis.sh └── reverse_ssh_tunnel.sh ├── json_t └── json_t.go ├── PROGRESS.md ├── examples ├── legacy_bash │ ├── README.md │ ├── rock_hello_world.sh │ ├── rock_hello_world_with_grep.sh │ ├── naval_fate.sh │ ├── cat-n_wrapper_example.sh │ ├── sshdiff_with_docopts.sh │ └── sshdiff_legacy.sh ├── README.md ├── without_docopts_sh_lib │ ├── double_dash.sh │ ├── quick_example.sh │ ├── counted_example.sh │ ├── rock_no-stdin_example.sh │ ├── calculator_example.sh │ ├── arguments_example.sh │ ├── rock_stdin_example.sh │ ├── cat-n_wrapper_example.sh │ └── naval_fate.sh ├── quick_example.sh ├── counted_example.sh ├── docopts_auto_example.sh ├── calculator_example.sh ├── rock_no-stdin_example.sh ├── naval_fate.sh ├── cat-n_wrapper_example.sh ├── arguments_example.sh └── rock_stdin_example.sh ├── docker ├── README.md ├── debian-docopts.Dockerfile └── update_go.sh ├── get_ldflags.sh ├── LICENSE-MIT ├── .github └── workflows │ └── ci.yml ├── docs ├── README.md ├── release.md ├── pre_built_binaries.md └── developer.md ├── API_proposal.md ├── Makefile ├── TODO.md ├── test_json_load └── test_json_load.go ├── language_agnostic_tester.py ├── testee.sh ├── common_input_test.json ├── get_docopts.sh ├── deployment.yml ├── docopts.sh ├── docopts_test.go ├── README.md ├── docopts.go └── testcases.docopt /tests/VERSION: -------------------------------------------------------------------------------- 1 | v0.6.3-rc2 2 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | v0.6.4-with-no-mangle-double-dash 2 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # Change history 2 | 3 | See deployment.yml 4 | 5 | -------------------------------------------------------------------------------- /tests/TODO.md: -------------------------------------------------------------------------------- 1 | # TODO on testing 2 | 3 | - test -V -h --help --version 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | docopts 2 | *.swp 3 | env 4 | build/docopts_* 5 | build/docopts.go 6 | -------------------------------------------------------------------------------- /tests/test_content.txt: -------------------------------------------------------------------------------- 1 | some include test content 2 | line 2 3 | 4 | line 4 5 | end 6 | -------------------------------------------------------------------------------- /tests/exit_handler.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exit_handler() { 3 | exit_code=$? 4 | echo "recieved exit $exit_code" 5 | } 6 | 7 | trap exit_handler EXIT 8 | 9 | -------------------------------------------------------------------------------- /ci/get_bats-core.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | git clone https://github.com/bats-core/bats-core.git --depth=1 4 | cd bats-core 5 | sudo ./install.sh /usr/local 6 | 7 | pwd 8 | type bats 9 | bats --version 10 | -------------------------------------------------------------------------------- /json_t/json_t.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/docopt/docopts/test_json_load" 6 | ) 7 | 8 | func main() { 9 | t, err := test_json_loader.Load_json("../common_input_test.json") 10 | if err != nil { 11 | fmt.Println(err.Error()) 12 | } else { 13 | for _, e := range t { 14 | fmt.Printf("%v\n", e.ToString()) 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /PROGRESS.md: -------------------------------------------------------------------------------- 1 | # current work in PROGRRESS 2 | 3 | ## releasing v0.6.3-rc2 4 | 5 | - finish release in docs/release.md 6 | - OK: tests/get_doctops.bats DRY previous VERSION 7 | - OK: fix: not ok 21 docopt_get_help_string on macos 8 | 9 | ## grammar programming 10 | 11 | Testing https://github.com/alecthomas/participle 12 | 13 | ## provide test on old environment 14 | 15 | docker? 16 | 32bits 17 | bash 3 18 | 19 | -------------------------------------------------------------------------------- /examples/legacy_bash/README.md: -------------------------------------------------------------------------------- 1 | # docopts legacy examples 2 | 3 | These examples work on bash 3.x by not using `docopts.sh` `--auto` mode. 4 | And without using associative arrays (bash 4.x only). 5 | 6 | ## Example of conversion 7 | 8 | `sshdiff_legacy.sh` doesn't use `docopts` at all. It has been converted by hand to `sshdiff_with_docopts.sh` as an 9 | example of rewrite legacy option parsing not using associative arrays. 10 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # docopts examples 2 | 3 | Thoses scripts are bash source code examples, they expect to find `docopts` binary in `$PATH`. 4 | All examples change their `$PATH` to first use the `docopts` built by go in the main project folder. 5 | 6 | Then: 7 | 8 | ``` 9 | cd to/the/examples/directory 10 | # and try any code localy: 11 | ./quick_example.sh --help 12 | ``` 13 | 14 | Change dir to subfolder, the same way, if you want to try other examples. 15 | -------------------------------------------------------------------------------- /tests/expect_build_doc.txt: -------------------------------------------------------------------------------- 1 | line 1 (should be empty for markdown, but for testing we only care numbers) 2 | [make README.md]: # (include test_content.txt) 3 | 4 | [source test_content.txt](test_content.txt) 5 | 6 | ``` 7 | some include test content 8 | line 2 9 | 10 | line 4 11 | end 12 | ``` 13 | 14 | line 12 15 | 16 | [make README.md]: # (echo "new outputed content") 17 | 18 | ``` 19 | new outputed content 20 | ``` 21 | 22 | some text after 23 | line 23 24 | -------------------------------------------------------------------------------- /tests/input_for_build_doc.txt: -------------------------------------------------------------------------------- 1 | line 1 (should be empty for markdown, but for testing we only care numbers) 2 | [make README.md]: # (include test_content.txt) 3 | 4 | [source link/to/file](link/to/file) # old markdown link 5 | 6 | ``` 7 | old input 8 | line 8 9 | line 9 10 | ``` 11 | 12 | line 12 13 | 14 | [make README.md]: # (echo "new outputed content") 15 | 16 | ``` 17 | previous command output here 18 | line 18 19 | line 19 20 | ``` 21 | 22 | some text after 23 | line 23 24 | -------------------------------------------------------------------------------- /examples/legacy_bash/rock_hello_world.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Example from README.md 3 | 4 | PATH=$PATH:../.. 5 | eval "$(docopts -V - -h - : "$@" <... 7 | 8 | Options: 9 | --verbose Generate verbose messages. 10 | --help Show help options. 11 | --version Print program version. 12 | ---- 13 | rock 0.1.0 14 | Copyright (C) 200X Thomas Light 15 | License RIT (Robot Institute of Technology) 16 | This is free software: you are free to change and redistribute it. 17 | There is NO WARRANTY, to the extent permitted by law. 18 | EOF 19 | )" 20 | 21 | if $verbose ; then 22 | echo "Hello, world!" 23 | fi 24 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/double_dash.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # vim: st=2 sw=2 sts=2 et: 3 | 4 | usage() { 5 | cat << EOU 6 | Test for docopts double-dash handling 7 | 8 | Usage: 9 | $0 cmd [options] [--] [...] 10 | $0 diff [-u] BEFORE AFTER 11 | $0 -h | --help 12 | 13 | Options: 14 | -p --platform= Platform to configure 15 | --trace Full trace output from bash 16 | -u unified diff 17 | EOU 18 | } 19 | 20 | # global mode 21 | # not needed if docopts already in PATH 22 | PATH=../..:$PATH 23 | parsed="$(docopts -G ARGS -h "$(usage)" : "$@")" 24 | echo "$parsed" 25 | eval "$parsed" 26 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/quick_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | usage() { 4 | cat << EOU 5 | Usage: 6 | quick_example.sh tcp [--timeout=] 7 | quick_example.sh serial [--baud=9600] [--timeout=] 8 | quick_example.sh -h | --help | --version 9 | 10 | Examples: 11 | ./quick_example.sh tcp remote-node 80 --timeout=120 12 | ./quick_example.sh serial 123 --timeout=120 13 | EOU 14 | } 15 | 16 | # if docopts is in PATH, not needed. 17 | PATH=../..:$PATH 18 | 19 | version='0.1.1rc' 20 | parsed=$(docopts -A myargs -h "$(usage)" -V $version : "$@") 21 | eval "$parsed" 22 | 23 | # main code 24 | for a in ${!myargs[@]} ; do 25 | echo "$a = ${myargs[$a]}" 26 | done 27 | -------------------------------------------------------------------------------- /examples/legacy_bash/rock_hello_world_with_grep.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Example from README.md 3 | PATH=$PATH:../.. 4 | 5 | #? rock 0.1.0 6 | #? Copyright (C) 200X Thomas Light 7 | #? License RIT (Robot Institute of Technology) 8 | #? This is free software: you are free to change and redistribute it. 9 | #? There is NO WARRANTY, to the extent permitted by law. 10 | 11 | ##? Usage: rock [options] ... 12 | ##? 13 | ##? Options: 14 | ##? --help Show help options. 15 | ##? --version Print program version. 16 | 17 | help=$(grep "^##?" "$0" | cut -c 5-) 18 | version=$(grep "^#?" "$0" | cut -c 4-) 19 | eval "$(docopts -h "$help" -V "$version" : "$@")" 20 | 21 | for arg in "${argv[@]}"; do 22 | echo "$arg" 23 | done 24 | -------------------------------------------------------------------------------- /examples/quick_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Usage: 4 | # quick_example.sh tcp [--timeout=] 5 | # quick_example.sh serial [--baud=9600] [--timeout=] 6 | # quick_example.sh -h | --help | --version 7 | # 8 | # Examples: 9 | # ./quick_example.sh tcp remote-node 80 --timeout=120 10 | # ./quick_example.sh serial 123 --timeout=120 11 | 12 | # if docopts is in PATH, not needed. 13 | PATH=..:$PATH 14 | 15 | libpath=../ 16 | source $libpath/docopts.sh 17 | 18 | help=$(docopt_get_help_string $0) 19 | version='0.1.1rc' 20 | 21 | parsed=$(docopts -A myargs -h "$help" -V $version : "$@") 22 | eval "$parsed" 23 | 24 | # main code 25 | for a in ${!myargs[@]} ; do 26 | echo "$a = ${myargs[$a]}" 27 | done 28 | -------------------------------------------------------------------------------- /examples/counted_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Usage: counted_example.sh --help 4 | # counted_example.sh -v... 5 | # counted_example.sh go [go] 6 | # counted_example.sh (--path=)... 7 | # counted_example.sh 8 | # 9 | # Try: counted_example.sh -vvvvvvvvvv 10 | # counted_example.sh go go 11 | # counted_example.sh --path ./here --path ./there 12 | # counted_example.sh this.txt that.txt 13 | 14 | # if docopts is in PATH, not needed. 15 | PATH=..:$PATH 16 | source docopts.sh --auto "$@" 17 | 18 | # docopt_auto_parse use ARGS bash 4 globla assoc array 19 | # main code 20 | # on assoc array '!' before nane gike hash keys 21 | for a in ${!ARGS[@]} ; do 22 | echo "$a = ${ARGS[$a]}" 23 | done 24 | -------------------------------------------------------------------------------- /examples/docopts_auto_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # sample of script using auto-parser. 4 | # 5 | # Usage: docopts_auto_example.sh [cmd] FILE... 6 | # 7 | # do something 8 | # do something 9 | # 10 | # Example: 11 | # ./docopts_auto_example.sh cmd file1 file2 12 | # ./docopts_auto_example.sh cmd file1 file2 13 | # ./docopts_auto_example.sh -h 14 | 15 | # Auto parse needs an empty line after the top comment above ^^^ 16 | 17 | # docopts.sh is compatible with 'set -u' 18 | set -u 19 | 20 | # if docopts is in PATH no need to change it. 21 | PATH=..:$PATH 22 | 23 | # auto parse this file. 24 | # That's all! 25 | source ../docopts.sh --auto "$@" 26 | 27 | # main code based on $ARGS produced by --auto 28 | for a in ${!ARGS[@]} ; do 29 | echo "$a = ${ARGS[$a]}" 30 | done 31 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/counted_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | usage() { 4 | cat << EOU 5 | Usage: counted_example.sh --help 6 | counted_example.sh -v... 7 | counted_example.sh go [go] 8 | counted_example.sh (--path=)... 9 | counted_example.sh 10 | 11 | Try: counted_example.sh -vvvvvvvvvv 12 | counted_example.sh go go 13 | counted_example.sh --path ./here --path ./there 14 | counted_example.sh this.txt that.txt 15 | EOU 16 | } 17 | 18 | # if docopts is in PATH, not needed. 19 | PATH=../..:$PATH 20 | eval "$(docopts -A ARGS -h "$(usage)" : "$@")" 21 | 22 | # docopt_auto_parse use ARGS bash 4 global assoc array 23 | # main code 24 | # on assoc array '!' before nane gike hash keys 25 | for a in ${!ARGS[@]} ; do 26 | echo "$a = ${ARGS[$a]}" 27 | done 28 | -------------------------------------------------------------------------------- /docker/README.md: -------------------------------------------------------------------------------- 1 | # Some docker container with docopts installed 2 | 3 | No special purpose than to recreate an environment with `docopts` 4 | 5 | ## Build the docker image 6 | 7 | The `--build-arg` is used to pass teh version of `docopts` to download pre-built binary. 8 | If you're publishing `docopts` the `../VERSION` may not yet reflect a published version. 9 | You can find previously published VERSION in `../tests/VERSION`. 10 | 11 | ``` 12 | docker build -f debian-docopts.Dockerfile --build-arg VERSION=$(cat ./VERSION) -t debian-docopts . 13 | ``` 14 | 15 | ## Run interactive 16 | 17 | You will have latest python version 0.6.1 in PATH as `docopts` and Go version in PATH too as `docopts0` 18 | 19 | Golang work space will also be installed if you want to test something. 20 | 21 | ``` 22 | docker run -it debian-docopts:latest 23 | ``` 24 | -------------------------------------------------------------------------------- /examples/calculator_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Not a serious example. 3 | # 4 | # Usage: 5 | # calculator_example.sh ( ( + | - | * | / ) )... 6 | # calculator_example.sh [( , )]... 7 | # calculator_example.sh (-h | --help) 8 | # 9 | # Examples: 10 | # calculator_example.sh 1 + 2 + 3 + 4 + 5 11 | # calculator_example.sh 1 + 2 '*' 3 / 4 - 5 # note quotes around '*' 12 | # calculator_example.sh sum 10 , 20 , 30 , 40 13 | # 14 | # Options: 15 | # -h, --help 16 | # 17 | # Example: 18 | # ./calculator_example.sh 30 + 23 - 22 19 | # 20 | 21 | source ../docopts.sh 22 | # not needed if docopts is in PATH 23 | PATH=..:$PATH 24 | 25 | help=$(docopt_get_help_string $0) 26 | version='0.1' 27 | parsed=$(docopts -A args -h "$help" -V $version : "$@") 28 | echo "$parsed" 29 | eval "$parsed" 30 | -------------------------------------------------------------------------------- /get_ldflags.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Usage: ./get_ldflags.sh [BUILD_FLAGS] 4 | # 5 | # This file has been created by: deploy.sh init -- 2020-04-04 08:27:32 6 | # 7 | # This script is an helper for both Makefile + deploy.sh 8 | # 9 | # You can reuse it in your Makefile: 10 | # BUILD_FLAGS=$(shell ./get_ldflags.sh) 11 | # your_target: your_target.go Makefile ${OTHER_DEP} 12 | # go build -o $@ -ldflags "${BUILD_FLAGS} ${LDFLAGS}" 13 | 14 | set -eu 15 | build_flags=${1:-} 16 | if [[ -z $build_flags ]]; then 17 | # govvv define main.Version with the contents of ./VERSION file, if exists 18 | build_flags=$(govvv -flags) 19 | fi 20 | 21 | # you can add more flags here: 22 | build_flags+=" -X 'main.GoBuildVersion=$(go version)' -X 'main.ByUser=${USER}'" 23 | 24 | # the last command MUST display all build_flags 25 | echo "$build_flags" 26 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/rock_no-stdin_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | usage() { 4 | cat << EOU 5 | Usage: rock [options] ... 6 | 7 | Options: 8 | --verbose Generate verbose messages. 9 | --help Show help options. 10 | --version Print program version. 11 | EOU 12 | } 13 | 14 | version="rock 0.1.0 15 | Copyright (C) 200X Thomas Light 16 | License RIT (Robot Institute of Technology) 17 | This is free software: you are free to change and redistribute it. 18 | There is NO WARRANTY, to the extent permitted by law." 19 | 20 | # no need to change PATH if docopts are in your PATH 21 | PATH=../..:$PATH 22 | 23 | parsed=$(docopts -V "$version" -h "$(usage)" : "$@") 24 | #echo "$parsed" 25 | eval "$parsed" 26 | 27 | echo "verbose=$verbose" 28 | 29 | if $verbose ; then 30 | echo "Hello, world!" 31 | else 32 | : 33 | # I'm silent 34 | fi 35 | -------------------------------------------------------------------------------- /examples/rock_no-stdin_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: rock [options] ... 3 | # 4 | # Options: 5 | # --verbose Generate verbose messages. 6 | # --help Show help options. 7 | # --version Print program version. 8 | 9 | version="rock 0.1.0 10 | Copyright (C) 200X Thomas Light 11 | License RIT (Robot Institute of Technology) 12 | This is free software: you are free to change and redistribute it. 13 | There is NO WARRANTY, to the extent permitted by law." 14 | 15 | # no need to change PATH if docopts.sh an docopts are in your PATH 16 | PATH=..:$PATH 17 | source docopts.sh 18 | help=$(docopt_get_help_string "$0") 19 | 20 | parsed=$(docopts -V "$version" -h "$help" : "$@") 21 | echo "$parsed" 22 | eval "$parsed" 23 | 24 | echo "verbose=$verbose" 25 | 26 | if $verbose ; then 27 | echo "Hello, world!" 28 | else 29 | : 30 | # I'm silent 31 | fi 32 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/calculator_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | usage() { 3 | cat << EOU 4 | Not a serious example. 5 | 6 | Usage: 7 | calculator_example.sh ( ( + | - | * | / ) )... 8 | calculator_example.sh [( , )]... 9 | calculator_example.sh (-h | --help) 10 | 11 | Examples: 12 | calculator_example.sh 1 + 2 + 3 + 4 + 5 13 | calculator_example.sh 1 + 2 '*' 3 / 4 - 5 # note quotes around '*' 14 | calculator_example.sh sum 10 , 20 , 30 , 40 15 | 16 | Options: 17 | -h, --help 18 | 19 | Example: 20 | ./calculator_example.sh 30 + 23 - 22 21 | EOU 22 | } 23 | 24 | # not needed if docopts is in PATH 25 | PATH=../..:$PATH 26 | version='0.1' 27 | parsed=$(docopts -A args -h "$(usage)" -V $version : "$@") 28 | echo "$parsed" 29 | echo "================================================================================" 30 | eval "$parsed" 31 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/arguments_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | usage() { 4 | cat << EOU 5 | Usage: arguments_example.sh [-vqrh] [FILE] ... 6 | arguments_example.sh (--left | --right) CORRECTION FILE 7 | 8 | Process FILE and optionally apply correction to either left-hand side or 9 | right-hand side. 10 | 11 | Arguments: 12 | FILE optional input file 13 | CORRECTION correction angle, needs FILE, --left or --right to be present 14 | 15 | Options: 16 | -h --help 17 | -v verbose mode 18 | -q quiet mode 19 | -r make report 20 | --left use left-hand side 21 | --right use right-hand side 22 | EOU 23 | } 24 | 25 | # not needed if docopts already in PATH 26 | PATH=../..:$PATH 27 | eval "$(docopts -A ARGS -h "$(usage)" : "$@")" 28 | 29 | # main code 30 | # on assoc array '!' before nane gike hash keys 31 | for a in ${!ARGS[@]} ; do 32 | echo "$a = ${ARGS[$a]}" 33 | done 34 | -------------------------------------------------------------------------------- /examples/naval_fate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Naval Fate. 3 | # 4 | # Usage: 5 | # naval_fate.sh ship new ... 6 | # naval_fate.sh ship move [--speed=] 7 | # naval_fate.sh ship shoot 8 | # naval_fate.sh mine (set|remove) [--moored|--drifting] 9 | # naval_fate.sh -h | --help 10 | # naval_fate.sh --version 11 | # 12 | # Options: 13 | # -h --help Show this screen. 14 | # --version Show version. 15 | # --speed= Speed in knots [default: 10]. 16 | # --moored Moored (anchored) mine. 17 | # --drifting Drifting mine. 18 | # 19 | 20 | 21 | # if docopts is in PATH, not needed. 22 | # Note: docopts.sh is also found in PATH 23 | PATH=..:$PATH 24 | 25 | VERSION='Naval Fate 2.0' 26 | source docopts.sh 27 | # no vesion support in docopt_auto_parse() so we call docopts directly 28 | usage=$(docopt_get_help_string "$0") 29 | eval "$(docopts -A ARGS -V "$VERSION" -h "$usage" : "$@")" 30 | 31 | docopt_print_ARGS 32 | -------------------------------------------------------------------------------- /ci/get_bash5_macos.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Only required for macos for unit testing advanced bash 4+ functionality and for bats. 4 | # 5 | # Our hack for macos bash version too old for unit testing 6 | 7 | pathshow () 8 | { 9 | local var=${1:-PATH}; 10 | eval "echo \$$var | tr : $'\n'" 11 | } 12 | 13 | if [[ "$RUNNER_OS" == "macOS" ]]; then 14 | if ((BASH_VERSINFO[0] <= 3)) ; then 15 | HOMEBREW_NO_AUTO_UPDATE=1 brew install bash # quick install, no brew update (c.f. https://apple.stackexchange.com/a/293252/167983) 16 | fi 17 | fi 18 | 19 | echo "======================= splited PATH" 20 | pathshow PATH 21 | 22 | echo "======================= new bash version" 23 | hash -r bash 24 | bash --version 25 | type bash 26 | 27 | # require sed macos comaptible regexp \+ doesn't exist ==> \{1,\} 28 | MY_BASH_VERSINFO=$(bash --version | sed -n -e '1 s/^.*version \([0-9.]\{1,\}\).*/\1/ p') 29 | if [[ ! $MY_BASH_VERSINFO =~ ^[4-9] ]] ; then 30 | echo "install bash5 failed" 31 | exit 1 32 | fi 33 | -------------------------------------------------------------------------------- /examples/cat-n_wrapper_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # cat -n all files 4 | # 5 | # Usage: cat-n_wrapper_example.sh [--count=N] FILE... 6 | # 7 | # Arguments: 8 | # FILE input file 9 | # 10 | # Options: 11 | # --count=N limit the number of line to display 12 | # 13 | # Examples: 14 | # ./cat-n_wrapper.sh --count=3 cat-n_wrapper.sh quick_example.sh 15 | # 16 | 17 | # no PATH changes required if docopts binary is in the PATH already 18 | PATH=..:$PATH 19 | source ../docopts.sh 20 | help=$(docopt_get_help_string $0) 21 | version='0.1' 22 | 23 | parsed=$(docopts -A args -h "$help" -V $version : "$@") 24 | #echo "$parsed" 25 | eval "$parsed" 26 | 27 | cat_limit() { 28 | if [[ -z "${args[--count]}" ]] ; then 29 | cat -n "$1" 30 | else 31 | cat -n "$1" | head -"${args[--count]}" 32 | fi 33 | } 34 | 35 | # current docopts multiple argument wrapper 36 | n=${args[FILE,#]} 37 | for i in $(seq 0 $(($n - 1))) 38 | do 39 | f="${args[FILE,$i]}" 40 | echo "----- $f -------" 41 | cat_limit "$f" 42 | done 43 | -------------------------------------------------------------------------------- /examples/arguments_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Usage: arguments_example.sh [-vqrh] [FILE] ... 4 | # arguments_example.sh (--left | --right) CORRECTION FILE 5 | # 6 | # Process FILE and optionally apply correction to either left-hand side or 7 | # right-hand side. 8 | # 9 | # Arguments: 10 | # FILE optional input file 11 | # CORRECTION correction angle, needs FILE, --left or --right to be present 12 | # 13 | # Options: 14 | # -h --help 15 | # -v verbose mode 16 | # -q quiet mode 17 | # -r make report 18 | # --left use left-hand side 19 | # --right use right-hand side 20 | # 21 | 22 | # if docopts is in PATH, not needed. 23 | # Note: docopts.sh is also found in PATH 24 | PATH=..:$PATH 25 | # auto parse the header above, See: docopt_get_help_string 26 | source docopts.sh --auto "$@" 27 | 28 | # docopt_auto_parse use ARGS bash 4 global assoc array 29 | # main code 30 | # on bash assoc array a '!' before name gives hash keys list 31 | for a in ${!ARGS[@]} ; do 32 | echo "$a = ${ARGS[$a]}" 33 | done 34 | -------------------------------------------------------------------------------- /examples/legacy_bash/naval_fate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Naval Fate. 3 | # 4 | # Usage: 5 | # naval_fate.sh ship new ... 6 | # naval_fate.sh ship move [--speed=] 7 | # naval_fate.sh ship shoot 8 | # naval_fate.sh mine (set|remove) [--moored|--drifting] 9 | # naval_fate.sh -h | --help 10 | # naval_fate.sh --version 11 | # 12 | # Options: 13 | # -h --help Show this screen. 14 | # --version Show version. 15 | # --speed= Speed in knots [default: 10]. 16 | # --moored Moored (anchored) mine. 17 | # --drifting Drifting mine. 18 | # 19 | 20 | 21 | # if docopts is in PATH, not needed. 22 | # Note: docopts.sh is also found in PATH 23 | PATH=../..:$PATH 24 | 25 | VERSION='Naval Fate 2.0' 26 | source docopts.sh 27 | # no version support in docopt_auto_parse() so we call docopts directly 28 | usage=$(docopt_get_help_string "$0") 29 | parsed="$(docopts -G ARGS -V "$VERSION" -h "$usage" : "$@")" 30 | echo "============ parsed output" 31 | echo "$parsed" 32 | # now vars are populated at global scope 33 | eval "$parsed" 34 | 35 | echo "============== docopt_print_ARGS" 36 | docopt_print_ARGS -G 37 | -------------------------------------------------------------------------------- /examples/rock_stdin_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Example reading from stdin 4 | # 5 | 6 | # no need to change PATH if docopts binary is in PATH already 7 | PATH=..:$PATH 8 | source ../docopts.sh 9 | 10 | # read both verion and usage from stdin 11 | # in global mode (option names arg mangled, See docopts --help 12 | parsed="$(docopts -V - -h - : "$@" <... 14 | 15 | Options: 16 | --verbose Generate verbose messages. 17 | --help Show help options. 18 | --version Print program version. 19 | ---- 20 | rock 0.1.0 21 | Copyright (C) 200X Thomas Light 22 | License RIT (Robot Institute of Technology) 23 | This is free software: you are free to change and redistribute it. 24 | There is NO WARRANTY, to the extent permitted by law. 25 | EOF 26 | )" 27 | 28 | eval "$parsed" 29 | 30 | echo "$parsed" 31 | echo "===========================" 32 | 33 | if $verbose ; then 34 | # env var are in global scope 35 | typeset -p argv help version 36 | fi 37 | 38 | # docopts will fail without 39 | if [[ -n $argv ]] ; then 40 | for body in ${argv[@]} 41 | do 42 | echo "let's rock $body" 43 | done 44 | fi 45 | 46 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/rock_stdin_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Example reading from stdin 4 | # 5 | 6 | # no need to change PATH if docopts binary is in PATH already 7 | PATH=../..:$PATH 8 | 9 | # read both verion and usage from stdin 10 | # in global mode (option names arg mangled, See docopts --help 11 | parsed="$(docopts -V - -h - : "$@" <... 13 | 14 | Options: 15 | --verbose Generate verbose messages. 16 | --help Show help options. 17 | --version Print program version. 18 | ---- 19 | rock 0.1.0 20 | Copyright (C) 200X Thomas Light 21 | License RIT (Robot Institute of Technology) 22 | This is free software: you are free to change and redistribute it. 23 | There is NO WARRANTY, to the extent permitted by law. 24 | EOF 25 | )" 26 | 27 | eval "$parsed" 28 | 29 | echo "$parsed" 30 | echo "===========================" 31 | 32 | if $verbose ; then 33 | # env var are in global scope 34 | typeset -p argv help version 35 | fi 36 | 37 | # docopts will fail without 38 | if [[ -n $argv ]] ; then 39 | for body in ${argv[@]} 40 | do 41 | echo "let's rock $body" 42 | done 43 | fi 44 | 45 | -------------------------------------------------------------------------------- /docker/debian-docopts.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:buster 2 | ENV DEBIAN_FRONTEND=noninteractive 3 | RUN apt-get update -y \ 4 | && apt-get install -y --no-install-recommends \ 5 | unzip \ 6 | wget \ 7 | python3-setuptools \ 8 | python3-pip \ 9 | vim \ 10 | git \ 11 | less 12 | 13 | RUN apt-get install -y --no-install-recommends \ 14 | sudo \ 15 | tar \ 16 | curl \ 17 | make 18 | 19 | WORKDIR /app 20 | COPY *.sh \ 21 | /app/ 22 | 23 | # install precompiled binary published ==> docopts0 24 | ARG VERSION 25 | RUN wget https://github.com/docopt/docopts/releases/download/$VERSION/docopts_linux_amd64 26 | RUN install -o root -g root -m a+x docopts_linux_amd64 /usr/local/bin/docopts0 27 | 28 | # install a golang build env 29 | # predownload the tgz so it get docker cached 30 | RUN wget --quiet https://dl.google.com/go/go1.17.1.linux-amd64.tar.gz 31 | RUN ./update_go.sh 32 | ENV PATH=$PATH:/app:/usr/local/go/bin:/root/go/bin 33 | 34 | RUN go get github.com/docopt/docopt-go && go get github.com/docopt/docopts 35 | 36 | # intall python version 0.6.1 ==> /usr/local/bin/docopts 37 | RUN pip3 install docopts 38 | 39 | # return to basic /app dir 40 | WORKDIR /app 41 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Vladimir Keleshev, 2 | Lari Rasku, 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the "Software"), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /docker/update_go.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Update from tarball 4 | # 5 | # Steps: 6 | # 1. change GOVER to the wanted version 7 | # 2. Run ./update_go.sh 8 | 9 | set -euo pipefail 10 | 11 | localgo=/usr/local/go 12 | gobin=$localgo/bin/go 13 | 14 | set +e 15 | actual_gover=$($gobin version | grep -o -E 'go[0-9.]+') 16 | set -e 17 | 18 | # change this go version for updating 19 | # can be copy/paste from the email announcing the version 20 | GOVER=go1.17.1 21 | 22 | if [[ $actual_gover == $GOVER ]] ; then 23 | echo "$gobin version: $actual_gover is the same as $GOVER" 24 | exit 0 25 | fi 26 | 27 | gotar="${GOVER}.linux-amd64.tar.gz" 28 | url="https://dl.google.com/go/$gotar" 29 | 30 | if [[ ! -e $gotar ]] ; then 31 | wget $url -O $gotar 32 | else 33 | echo "using local file: $gotar" 34 | fi 35 | 36 | if [[ -d $localgo ]] ; then 37 | old=/usr/local/go.old 38 | if [[ -d $old ]] ; then 39 | echo "old dest exists, remove it: $old" 40 | exit 1 41 | fi 42 | 43 | echo "moving go to $old" 44 | sudo mv $localgo $old 45 | fi 46 | 47 | if [[ ! -d $localgo ]] ; then 48 | dest=/usr/local 49 | echo "extracting $gotar to $dest" 50 | sudo tar -C $dest -xzf $gotar 51 | fi 52 | 53 | echo "Go updated:" 54 | $localgo/bin/go version 55 | 56 | echo "add $localgo/bin to your PATH" 57 | -------------------------------------------------------------------------------- /examples/legacy_bash/cat-n_wrapper_example.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # cat -n all files 4 | # 5 | # Usage: cat-n_wrapper_example.sh [--count=N] FILE... 6 | # 7 | # Arguments: 8 | # FILE input file, if FILE equal - stdin is used instead. 9 | # 10 | # Options: 11 | # --count=N limit the number of line to display 12 | # 13 | # Examples: 14 | # ./cat-n_wrapper.sh --count=3 cat-n_wrapper.sh quick_example.sh 15 | # 16 | 17 | # no PATH changes required if docopts binary is in the PATH already 18 | PATH=../..:$PATH 19 | source ../../docopts.sh 20 | help=$(docopt_get_help_string $0) 21 | version='0.1' 22 | 23 | parsed=$(docopts -G args -h "$help" -V $version : "$@") 24 | # Show parsed arguments 25 | #echo "$parsed" 26 | eval "$parsed" 27 | 28 | cat_limit() { 29 | local filename=$1 30 | 31 | if [[ -z "$args_count" ]] ; then 32 | cat -n "$filename" 33 | else 34 | cat -n "$filename" | head -"$args_count" 35 | fi 36 | } 37 | 38 | # array len in bash 39 | n=${#args_FILE[@]} 40 | for i in $(seq 0 $(($n - 1))) 41 | do 42 | f="${args_FILE[$i]}" 43 | echo "----- $f ------- $((i+1)) / $n" 44 | if [[ $f == '-' ]] ; then 45 | f=/dev/stdin 46 | fi 47 | if [[ -f $f ]] 48 | then 49 | cat_limit "$f" 50 | else 51 | echo "file not found: $f" 52 | fi 53 | done 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: 4 | branches: [master] 5 | push: 6 | branches: [docopts-go, master] 7 | 8 | jobs: 9 | ci: 10 | name: CI - Go ${{ matrix.go-version }}, ${{ matrix.os }} 11 | 12 | runs-on: ${{ matrix.os }} 13 | strategy: 14 | matrix: 15 | go-version: ["1.14"] 16 | os: [ubuntu-latest, macOS-latest] 17 | 18 | steps: 19 | - name: Checkout repo 20 | uses: actions/checkout@v3 21 | - name: Setup Go ${{ matrix.go-version }} 22 | uses: actions/setup-go@v3 23 | with: 24 | go-version: ${{ matrix.go-version }} 25 | # cache: true # TODO: cache needs path to go.sum per https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs 26 | - name: Install 27 | run: | 28 | bash --version ; type bash 29 | # hack for speedup on mac build with our own bash5 30 | ./ci/get_bash5_macos.sh 31 | ./ci/get_bats-core.sh 32 | go get github.com/docopt/docopt-go 33 | # get our official repos too 34 | go get github.com/docopt/docopts 35 | - name: Test 36 | run: make test 37 | 38 | # For debugging, create reverse SSH tunnel (this should be run on failure too) 39 | # - name: Debug 40 | # if: runner.os == "macOS" 41 | # run: bash -x ./ci/reverse_ssh_tunnel.sh 42 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/cat-n_wrapper_example.sh: -------------------------------------------------------------------------------- 1 | 2 | usage() { 3 | cat << EOU 4 | 5 | cat -n all files 6 | 7 | Usage: cat-n_wrapper_example.sh [--count=N] FILE... 8 | 9 | Arguments: 10 | FILE input file, - is converted to stdin 11 | 12 | Options: 13 | --count=N limit the number of line to display 14 | 15 | Examples: 16 | ./cat-n_wrapper_example.sh --count=3 cat-n_wrapper.sh quick_example.sh 17 | # read from standard input with - 18 | ./cat-n_wrapper_example.sh --count=3 - < cat-n_wrapper_example.sh 19 | EOU 20 | } 21 | 22 | # no PATH changes required if docopts binary is in the PATH already 23 | PATH=../..:$PATH 24 | help=$(usage) 25 | version='0.2' 26 | 27 | parsed=$(docopts -A args -h "$help" -V $version : "$@") 28 | #echo "$parsed" 29 | eval "$parsed" 30 | 31 | cat_limit() { 32 | if [[ -z "${args[--count]}" ]] ; then 33 | cat -n "$1" 34 | else 35 | cat -n "$1" | head -"${args[--count]}" 36 | fi 37 | } 38 | 39 | # current docopts multiple argument wrapper 40 | 41 | # use intermediate variable FILE_DASH to help vim syntax highlighting 42 | # you can put FILE,# as assoc key too. 43 | FILE_DASH='FILE,#' 44 | n=${args[$FILE_DASH]} 45 | for i in $(seq 0 $(($n - 1))) 46 | do 47 | f="${args[FILE,$i]}" 48 | if [[ $f == '-' ]] ; then 49 | f=/dev/stdin 50 | fi 51 | echo "----- $f -------" 52 | cat_limit "$f" 53 | done 54 | -------------------------------------------------------------------------------- /examples/legacy_bash/sshdiff_with_docopts.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # Show file differences between 2 hosts. 4 | # Usage: sshdiff.sh [-h] [-s] HOST1 HOST2 FILENAME [LINES_CONTEXT] 5 | # 6 | # Use colordiff if available. 7 | # 8 | # Options: 9 | # -h display this help and exit 10 | # -s use sort instead of cat to show remote FILENAME 11 | # 12 | # If not specified, LINES_CONTEXT defaults to 3. 13 | # 14 | # Environment variable: 15 | # EXTRA_DIFF pass extra argument to diff command 16 | # SSH_USER use this user for remote ssh user (default: $USER) 17 | # 18 | # Examples: 19 | # sshdiff.sh server1 server2 /etc/crontab 20 | # # ignore changes on blank lines 21 | # EXTRA_DIFF=-b sshdiff.sh webserver1 webserver2 /etc/apache2/apache2.conf 22 | 23 | # The line above must be empty 24 | PATH=../..:$PATH 25 | source docopts.sh --auto -G "$@" 26 | 27 | docopt_print_ARGS -G 28 | 29 | # converted main code 30 | FILTER="cat" 31 | if [ -z "$AGRS_LINES_CONTEXT" ]; then 32 | ARGS_LINES_CONTEXT=3 33 | fi 34 | 35 | # selecting diff program 36 | DIFF="$(which diff)" 37 | COLORDIFF="$(which colordiff)" 38 | if [ -x "$COLORDIFF" ]; then 39 | DIFF="$COLORDIFF" 40 | fi 41 | 42 | if [ -z "$SSH_USER" ]; then 43 | SSH_USER=$USER 44 | fi 45 | 46 | # complete final command 47 | "$DIFF" $EXTRA_DIFF -U "$ARGS_LINES_CONTEXT" \ 48 | <(ssh $SSH_USER@"$ARGS_HOST1" $FILTER "$ARGS_FILENAME") \ 49 | <(ssh $SSH_USER@"$ARGS_HOST2" $FILTER "$ARGS_FILENAME") 50 | 51 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # `docopts` Documentation 2 | 3 | `docopts` is a standalone binary program that generate bash code suitable for `eval`. 4 | 5 | See: [README.md](../README.md) 6 | 7 | ## `doctops.sh` bash library helper 8 | 9 | `doctops.sh` is a library companion of `doctops` binary. 10 | 11 | It may become the main entry point using `doctops`, but keep in mind that `doctops` is also a standalone binary. 12 | 13 | `doctops.sh` is intensively used in [../examples/](../examples/). 14 | 15 | It has 2 behaviors: 16 | 17 | * a library `source doctops.sh` (found in your `$PATH`) 18 | * a wrapper that auto parse option from a dedicated `Usage:` comment from your header script 19 | 20 | ### `doctops.sh` as a library 21 | 22 | No action is performed the functions and globals are now part as your bash environment. 23 | 24 | You can manualy call the lib functions. 25 | 26 | [make README.md]: # (sed -n -e '/^# Doc:$/,/^docopt_/ p' docopts.sh) 27 | 28 | ``` 29 | docopt_get_help_string() 30 | docopt_get_version_string() 31 | docopt_get_values() 32 | docopt_get_eval_array() 33 | docopt_get_raw_value() 34 | docopt_print_ARGS() 35 | ``` 36 | 37 | ### `docopts.sh` as a wrapper 38 | 39 | Automaticaly parse and eval the `Usage:` header of your script and build `docopts` call for you. 40 | The script will fail if option parse error are encountered. 41 | 42 | Generate Bash 4.0 associative array `${ARGS[--option]}`: 43 | 44 | ``` 45 | source docopts.sh --auto "$@" 46 | ``` 47 | 48 | Generate Bash mangled GLOBALS `$ARGS_option`: 49 | 50 | ``` 51 | source docopts.sh --auto -G "$@" 52 | ``` 53 | -------------------------------------------------------------------------------- /examples/without_docopts_sh_lib/naval_fate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | usage() { 4 | cat << EOU 5 | Naval Fate. 6 | 7 | Usage: 8 | naval_fate.sh ship new ... 9 | naval_fate.sh ship move [--speed=] 10 | naval_fate.sh ship shoot 11 | naval_fate.sh mine (set|remove) [--moored|--drifting] 12 | naval_fate.sh -h | --help 13 | naval_fate.sh --version 14 | 15 | Options: 16 | -h --help Show this screen. 17 | --version Show version. 18 | --speed= Speed in knots [default: 10]. 19 | --moored Moored (anchored) mine. 20 | --drifting Drifting mine.EOU 21 | EOU 22 | } 23 | 24 | # copied from docopts.sh 25 | # Debug, prints env varible ARGS or $1 formated as a bash 4 assoc array 26 | docopt_print_ARGS() { 27 | local assoc="$1" 28 | if [[ -z $assoc ]] ; then 29 | assoc=ARGS 30 | fi 31 | 32 | # bash dark magic copying $assoc argument to a local myassoc array 33 | # inspired by: 34 | # https://stackoverflow.com/questions/6660010/bash-how-to-assign-an-associative-array-to-another-variable-name-e-g-rename-t#8881121 35 | declare -A myassoc 36 | eval $(typeset -A -p $assoc|sed "s/ $assoc=/ myassoc=/") 37 | 38 | # loop on keys 39 | echo "docopt_print_ARGS => $assoc" 40 | local a 41 | for a in ${!myassoc[@]} ; do 42 | printf "%20s = %s\n" $a "${myassoc[$a]}" 43 | done 44 | } 45 | 46 | VERSION='Naval Fate 2.0' 47 | 48 | # if docopts is in PATH, not needed. 49 | PATH=../..:$PATH 50 | # auto parse the header above, See: docopt_get_help_string 51 | eval "$(docopts -A ARGS -h "$(usage)" -V "$VERSION" : "$@")" 52 | 53 | docopt_print_ARGS 54 | -------------------------------------------------------------------------------- /API_proposal.md: -------------------------------------------------------------------------------- 1 | # New API proposal for docopts - docopt on shell (bash) 2 | 3 | ## Update: 2021-09-16 4 | 5 | [docopts.sh](docopts.sh) has been publised as a `docopts` companion its documentation live here: 6 | [docs/README.md](docs/README.md). 7 | 8 | It offer some API alternatives and allow to build external wrapper. 9 | 10 | ## new cli API proposal for 2021 11 | 12 | * use verb for action 13 | * stop using `-h` `` for reading help 14 | * keep compatibily with old syntax trough a new verb: `compat` 15 | * introduce more useful error message 16 | 17 | 18 | ### new verb 19 | 20 | ### replace `-h` 21 | 22 | ``` 23 | Usage: 24 | docopts [options] parse USAGE : [...] 25 | docopts [options] parse [--no-declare] -A USAGE : [...] 26 | docopts [options] parse -G USAGE : [...] 27 | ``` 28 | 29 | Example: 30 | 31 | ``` 32 | docopts parse "$USAGE" :