Introduction
I'm using docopts in my script for parsing args from a help message.
The recommended usage is to use eval to run the command. I assume that's because docopts sets parsed args as variables that will be made available to the calling script.
Simplified example:
#!/usr/bin/env bash
function parse() {
eval "$(docopts -h "Usage: your_program <arg> [--verbose]" : "$@")"
}
# Parse some args
parse "some arg" --verbose
# Verify that the variable $arg was correctly set by docopts
if [ -z "${arg+x}" ]; then
echo "\$arg is not set!"
else
echo "the value of \$arg is \"$arg"\"
fi
Output: the value of $arg is "some arg"
Problem
When the passed arguments aren't valid, a usage message is printed to stderr and the exit code is set to 64. I need to capture this usage message into a variable so I can further process it.
Partial solution
Running in a subshell and capturing stderr does just that:
#!/usr/bin/env bash
function parse() {
usage_error_msg=$(eval "$(./docopts -h "Usage: your_program <arg> [--verbose]" : "$@")" 2>&1)
exit_code=$?
# Exit the program with an error if the usage isn't valid
(( exit_code == 0 )) || { echo "$usage_error_msg"; exit $exit_code; }
}
# Parse some args
parse "some arg" --verbose
# Verify that the variable $arg was correctly set by docopts
if [ -z "${arg+x}" ]; then
echo "\$arg is not set!"
else
echo "the value of \$arg is \"$arg"\"
fi
This correctly captures the output and exit_code and makes the script exit with a usage message if I omit a required argument (last line in the function).
However, the variables are no longer available in the script;
Output: $arg is not set!
Question
How can I capture stdout in a variable while still having the variables that docopts sets available in my script? The answers on SO for capturing output into a variable always seem to involve a subshell.
NOTE: I include docopts as a library so I'm also open to solutions that involve modifying the docopts Python script, although I prefer a pure bash solution (I know little about Python and don't want to complicate upgrading the docopts dependency in the future).
UPDATE:
After trying glenn jackman's answer I found out that docopts prints to stdout what eval should execute;
In case of valid syntax:
verbose=true
arg='some arg'
In case of invalid syntax:
echo 'Usage: your_program <arg> [--verbose]' >&2
exit 64
Docopts will always exit with 0, unless the syntax for the docopts command itself is incorrect. Otherwise the non-zero exit code won't be set until eval is executed. I imagine all programs that rely on this principle work like this.
Another thing missing in the problem section of my original question that I should clarify, is that the program exits when eval executes the output that results from passing args with invalid syntax to docopts. So when eval runs the code from the second example, the entire script exits.