andsens / docopt.sh Goto Github PK
View Code? Open in Web Editor NEWCommand-line argument parser for bash 3.2, 4+, and 5+.
License: MIT License
Command-line argument parser for bash 3.2, 4+, and 5+.
License: MIT License
When declaring array vars SC2154 must be disabled, since they are not declared through an eval meaning shellcheck can discover our trickery.
Options may switch places in the AST at times, resulting differing output for the same $DOC
. This is likely because dict
is used, OrderDict
should be used instead.
When passing escaped variables in the DOC they are not escaped once output is assigned.
Following docopt/docopts#35 : this is a twin issue with the project docopts - twin docopt/docopts#36
docopts
use can use global vars with or without prefix the names in the option are converted to mangled names (code here) (which is compatible to old python's output, old code here)
Current docopt.sh code
docopts README describing the manged argument conversion.
In order to improve compatibility we need to output compatible variables name from docopt.sh
Usage could be:
DOC="
Usage: pipo new [--switch <value>]
"
DOCOPT_COMPAT_DOCOPTS=mangled
docopt "$@"
compatible Outputs:
new=true
switch=true
value='some_value'
0.9.16 causes the shellcheck disable=
codes to be output e.g. like this # shellcheck disable=2016,10901091,2034
How do you install without pip?
Hi there!
I'm not very sure if the following should be ever possible, but we faced with this little problem and thought may worth to open an issue :)
We'd like to tell somehow, that the generated parser stops processing options. I don't know if using a delimiter like --
could help.
The use case is an script that's using docopt.sh
to parse just a piece of the options given, and internally is calling an underlying command with some other options passed to the script's CLI:
resticw -p profileX stats latest --cache-dir=/tmp/cache
The goal would be to separate somehow the options that are for the outer script (the ones that need to be parsed), to the rest of the line. So in the above example, only the -p profileX
option would be parseable by docopt.sh
, while the rest should not.
When running the above command, we get the usage help error, as in the example --cache-dir
is not something defined as an option for the script (full DOC below).
The following works just fine:
resticw -p profileX stats latest
Thanks!
DOC="A little wrapper over restic just to handle profiles and environment loading.
Usage:
resticw [options] <restic_arguments_line>...
The <restic_arguments_line> is just the regular unwrapped restic command arguments, e.g. stats latest
Options:
-p --profile=<name> Specify the profile to load or use default [default: default].
Examples:
resticw --profile profileA snapshots
resticw stats latest # this will use the profile: default
"
Reproduce:
#!/usr/bin/env bash
docopt_repro() {
DOC='
docopt.sh bug reproduce
Usage:
docopt-repro [-a] command
docopt-repro [-a]
'
eval "$(docopt "$@")"
return
}
docopt_repro "$@"
Thank you for this library, it is really useful.
It was a bit surprising that -h
and --help
both work, but not -v
or -V
in addition to --version
. Would be nice for the short version to be supported as well.
docopt_run()
exits too late when the source
command fails with docopt_parse: command not found
. Make sure to wrap the source
in an if and exit earlier.
The regular expression used to find the DOC string does not account for escaped quotes of any kind. Figure out if regex is still the way to go, or if a more reliable alternative should be implemented instead.
Detection of DOC=
should be able to handle single quotes as well as double quotes.
I've just found DOCOPT and I love it!
This is just a question, maybe even a nonsense :)
I was wondering if is there any pattern to use docopt.sh as a project dependency or on contrary is this kind of use discouraged. I couldn't manage to make it work in this way.
I mean, include docopt.sh repository as a Git submodule into another project to allow regenerating the parser on-the-fly and reacting to changes on the $DOC
without requiring neither to install docopt separately via pip3
, nor run it explicitly from command-line ($ docopt.sh SCRIPT
).
The aim would be to be able to use someting like: eval "$(./relative/path/to/docopt.sh --parser "$0")"
in a script, as the docopt.sh would be present as Git submodule.
If I'm not wrong, this would keep the script clean from parser-generated code. Sure for this to work, the script should live in its Git repository to maintain access to the parser (so no portable script). The aim would only be to allow other developers to quick modify the DOC options & run transparently without extra steps, always shipping the script as a Git repository.
Running docopt over this help string:
DOC="Upload Tool.
Usage:
./upload.sh [-h]
Options:
-h Print this help string
"
I'm getting a littany of shellcheck errors. SC1009 for a parse error on "parse_long". SC1056 for a "expected }". An undefined error on failure to parse this line left=("${left[@]:0:$i}" "${left[@]:((i+1))}")
.
Despite these errors, the script runs fine, so either the minification confuses shellcheck. Unsure how to resolve if this is a shellcheck problem unable to properly parse the minified parser.
The newline escape \
is not interpreted as expected by bash here (end of 2nd line):
done;if declare -p var_FIELD >/dev/null 2>&1; then eval $p'FIELD=("${var_FIELD'\
'[@]}")';else eval $p'FIELD=()';fi;eval $p'__purpose=${var___purpose:-'"'"'\
retrieve "$ITEMNAME"'"'"'};'$p'__mechanism=${var___mechanism:-both};'$p'__cach'\
The single line quoting should take effect here so the string is closed before we continue to the new line.
From:
DOC="Output Bitwarden item fields as bash variables
Usage:
bitwarden-fields [options] ITEMNAME [FIELD...]
Options:
-p --purpose PURPOSE Specify why the master password is required.
The text will be appended to
'Enter your Bitwarden Master Password to ...'
[default: retrieve \"\$ITEMNAME\"]
-m --mechanism=MECH Use \"bw\" or \"cache\" to retrieve the item [default: both]
--cache-for=SECONDS Cache item with socket-credential-cache [default: 0]
-j --json Output as JSON instead of bash variables
--prefix=PREFIX Prefix variable names with supplied string
-e Output exit codes as eval code (e.g. \"(exit 2)\")
--debug Turn on bash -x
Note:
To retrieve attachments, prefix their name with \`attachment:\`
For attachment IDs use \`attachmentid:\`
To retrieve all fields, omit the FIELD argument entirely
"
# docopt parser below, refresh this parser with `docopt.sh bitwarden-fields`
# shellcheck disable=2016,2086,2317,1090,1091,2034,2154
docopt() { source "$pkgroot/.upkg/docopt-lib.sh/docopt-lib.sh" '2.0.0' || {
ret=$?;printf -- "exit %d\n" "$ret";exit "$ret";};set -e
trimmed_doc=${DOC:0:933};usage=${DOC:47:55};digest=9e0b5;options=('-p --purpos'\
'e 1' '-m --mechanism 1' ' --cache-for 1' '-j --json 0' ' --prefix 1' '-e 0' \
' --debug 0');node_0(){ value __purpose 0;};node_1(){ value __mechanism 1;}
node_2(){ value __cache_for 2;};node_3(){ switch __json 3;};node_4(){ value \
__prefix 4;};node_5(){ switch _e 5;};node_6(){ switch __debug 6;};node_7(){
value ITEMNAME a;};node_8(){ value FIELD a true;};node_9(){ optional 0 1 2 3 4 \
5 6;};node_10(){ optional 11;};node_11(){ repeatable 8;};node_12(){ sequence 9 \
7 10;};cat <<<' docopt_exit() { [[ -n $1 ]] && printf "%s\n" "$1" >&2;printf \
"%s\n" "${DOC:47:55}" >&2;exit 1;}';local varnames=(__purpose __mechanism \
__cache_for __json __prefix _e __debug ITEMNAME FIELD) varname;for varname in \
"${varnames[@]}"; do unset "var_$varname";done;parse 12 "$@";local \
p=${DOCOPT_PREFIX:-''};for varname in "${varnames[@]}"; do unset "$p$varname"
done;if declare -p var_FIELD >/dev/null 2>&1; then eval $p'FIELD=("${var_FIELD'\
'[@]}")';else eval $p'FIELD=()';fi;eval $p'__purpose=${var___purpose:-'"'"'\
retrieve "$ITEMNAME"'"'"'};'$p'__mechanism=${var___mechanism:-both};'$p'__cach'\
'e_for=${var___cache_for:-0};'$p'__json=${var___json:-false};'$p'__prefix=${va'\
'r___prefix:-};'$p'_e=${var__e:-false};'$p'__debug=${var___debug:-false};'$p'I'\
'TEMNAME=${var_ITEMNAME:-};';local docopt_i=1;[[ $BASH_VERSION =~ ^4.3 ]] && \
docopt_i=2;for ((;docopt_i>0;docopt_i--)); do for varname in "${varnames[@]}"; \
do declare -p "$p$varname";done;done;}
docopt.sh version: 0.9.17
Hi @andsens, I've observed that when setting the following in scripts that are using docopt.sh, they fail:
set -u
or usually
set -euo pipefail
I tried to reproduce it with the demo in the README, so just setting the above flag after the shebang, then run and the following error shows when the script is run:
❯ ./naval.sh
./naval.sh: line 85: best_match_idx: unbound variable
./naval.sh: line 125: move: unbound variable
❯ ./naval.sh Olympia move 1 5 --speed 8
./naval.sh: line 82: match_count: unbound variable
./naval.sh: line 125: move: unbound variable
I'm usually adding the set -eu ...
as good practice for scripts. Is that incompatible in some sense with docopt or is it an issue?
Cheers!
stderr from shasum should be quenched unless the doc check fails.
I think it would nice to have a built-in mechanism to dump all recognized options and their parsed values. This could be a function included with the parser (say if DOCOPT_INCLUDE_DEBUG=true). I think it will be helpful to someone inexperienced in bash scripting, as well as a good way to show what variables are being defined etc. I would suggest including the option name, the variable name, and its value.
It seems your parser doesn't use python docopt for parsing bash $DOC
I also realized than docopt installed from pip install docopt
(0.6.2) is different from the code available on github.
Could you please explain the way you parse bash $DOC
usage from bash script, and the goal of each class?
I started to analyze you code, parsing:
Script
object which use a Doc
object to extract docopt part from the bash source script
Doc()
extracts bash $DOC
string which is matched from this regexpDoc
holds extracted part of the docopt stringScript()
object then passes the doc
instance to Guards
object which I don't know the role yet.Characters like `
need to be escaped with a \
in double quoted docs. This will cause docopt.sh to generate the wrong hash on parser generation. The docstring should be parsed instead and then hashed.
Because the doc is trimmed and the digest is generated from the trimmed version, appending to the doc will not cause the doc check to fail.
The solution is to use the raw $DOC
Thanks a lot for this script! Really useful.
I use it on Centos7 / Summit and shasum is not there. You should use instead sha256
.
Thanks again,
Thomas
PS: This is the fix I implemented. I can submit a PR if you want
if [[ -x "$(command -v shasum)" ]]; then
doc_hash=$(printf "%s" "$DOC" | shasum -a 256)
elif [[ -x "$(command -v shas256sum)" ]]; then
doc_hash=$(printf "%s" "$DOC" | shasum)
fi
When including docopt as a lib SC1090 should be ignored. More often than not the source is non-constant.
docopt.sh should allow indented DOC=
strings and detect other script parts that are indented as well, so that commands that are just functions (e.g. included in .bashrc) don't require weird indentation levels.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.