捕获eval命令的输出并保留变量

时间:2017-09-13 18:20:10

标签: bash shell

简介

我在我的脚本中使用docopts来解析帮助消息中的args。

recommended usage是使用eval来运行命令。我认为这是因为docopts将解析的args设置为可供调用脚本使用的变量。

简化示例:

#!/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

输出: the value of $arg is "some arg"

问题

当传递的参数无效时,会向stderr打印一条用法消息,退出代码设置为64.我需要将此用法消息捕获到变量中,以便我可以进一步处理它。

部分解决方案

在子shell中运行并捕获stderr就是这样:

#!/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

如果我省略了一个必需的参数(函数的最后一行),这就正确地捕获了输出和exit_code,并使用一条用法消息退出脚本。

但是,脚本中不再提供变量;

输出: $arg is not set!

问题

如何在我的脚本中仍然拥有docopts集可用的变量的同时捕获变量中的stdout?关于将输出捕获到变量的SO的答案似乎总是涉及子shell。

注意:我将docopts作为一个库包含在内,所以我也开放了涉及修改docopts Python脚本的解决方案,虽然我更喜欢纯粹的bash解决方案(我对Python知之甚少并且不想复制将来的docopts依赖关系变得复杂。)

UPDATE:

在尝试glenn jackman的答案后,我发现docopts打印到stdout应该执行什么样的eval;

如果语法有效

verbose=true
arg='some arg'

如果语法无效

echo 'Usage: your_program <arg> [--verbose]' >&2
exit 64

除非docopts命令本身的语法不正确,否则Docop将始终以0退出。否则,在执行eval之前,不会设置非零退出代码。我想所有依赖这个原理的程序都是这样的。

我应该澄清的原始问题的问题部分中缺少的另一个问题是,当eval执行将带有无效语法的args传递给docopts时产生的输出时,程序退出。因此,当eval运行第二个示例中的代码时,整个脚本将退出。

1 个答案:

答案 0 :(得分:1)

尝试延迟eval,直到您知道自己取得了成功:

function parse() {

    output=$(./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 "$output"; exit $exit_code; }

    eval "$output"
}

或者,结构略有不同

function parse() {
    if output=$(./docopts -h "Usage: your_program <arg> [--verbose]" : "$@" 2>&1)
    then
        eval "$output"
    else
        exit_code=$?
        echo "$output"
        exit $exit_code
    fi
}