五 bash选项的保存修改和还原

五 bash选项的保存修改和还原

【动机】

众所周知, bash 选项 errexit(默认为 off) 如果被设置为 on, 意味着只要主进程中任何一个函数(或命令)调用的退出码 (exitcode) 非0, 程序马上退出, 这对提高编码的健壮性的作用不言而喻.

例如, 下面的代码:

    maybe_error_fun(){
        echo "This function maybe throw an error..."
        return 1
    }

    motive(){
        echo "step 1....."
        maybe_error_fun
        echo "step 2...."
    }
    
    set -e
    motive

执行结果:

step 1.....
This function maybe throw an error...

motive 函数的第二句 echo “step 2…” 没有执行. 但是事情总有两面性, 如果我们想忽略 maybe_error_fun 的错误, 让它不影响后面的语句执行, 又应该怎么办呢?

最简单的方法, 是在 maybe_error_fun 调用上稍微做点手脚, 写成:

maybe_error_fun || :

可达目的. 可是这仅仅适用于 errexit 选项, 本文讨论的是如何通过临时修改选项然后还原达到目的.

考虑到 motive 函数, 不能依赖, 同时也要负责任的还原 shell 的 errexit 的配置, 利用 bash 选项的获取与设置, 修改 motive 函数, 得到下面的代码:

    motive1(){
        echo "step 1....."
        local orig_ee
        tstatx -t errexit  -n orig_ee
        tstatx -t errexit -v off # 或者 set +e
        maybe_error_fun 
        echo "step 2...."
        tstatx -t errexit -v $orig_ee
        maybe_error_fun
        echo "step 3..."
    }

执行结果:

step 1.....
This function maybe throw an error...
step 2....
This function maybe throw an error...

打印 step 2…, 说明修改成功, 而不打印 step 3…, 说明还原成功. 该方案可用, 似乎不够优雅.

【意图】

结合栈的实现, 以及 bash 选项的获取与设置, 封装选项的修改和还原.

【实现】

第四篇的 bash 选项的获取与设置的 options.sh 中, 添加函数 pusht, 用于将指定选项入栈, 并支持一次入栈多个选项:

:<<COMMENT
    将一个或多个 Option 的状态值推入栈
    $*: 指定的 option 名称列表
    usage: pusht <opt1> <opt2> ...
    返回值: 无 
COMMENT
pusht(){
    local opt curr
    for opt in "$@";do 
        tstatx -t $opt -n curr
        push $curr
    done
}

出栈函数 popt, 也支持多个选项, 注意顺序应与 pusht 的选项相反:

:<<COMMENT
    将栈中数据, 依次弹出, 并依次赋值给一个或多个 option 
    $*: 指定的 option 名称列表
    usage: popt <opt1> <opt2> ...
    返回值: 无 
COMMENT
popt(){
    local opt value 
    for opt in "$@";do
        # 空值检查的原因, 在于赋值给 value, 则 tstatx 调用因为缺少 -v 
        # 导致设置变为获取. 
        peek value   
        if [ "$value" ];then
            tstatx -t $opt -v "$value"
            etop
        fi
    done
}

由于 errexit 的修改还原操作是如此频繁, 为此专门创建函数 pushee / popee:

:<<COMMENT
    将 errexit 的状态值推入栈
    usage: pushee
    返回值: 无 
COMMENT
pushee(){
    pusht errexit
}
:<<COMMENT
    将栈中数据, 弹出到 errexit
    usage: popee
    返回值: 无 
COMMENT
popee(){
    popt errexit
}

【测试】

新建文件 test.sh, 先测试 pusht / popt

    # 根据实际文件位置修改路径
    source ./stack.sh
    source ./options
    way1(){
        check(){
            echo -e "\t\tcheck step $1..."
            shopt -o -p allexport
            shopt -o -p hashall
            shopt -p checkjobs
            shopt -p checkwinsize
        }

        check 1

        pusht allexport hashall checkjobs checkwinsize
        echo -e "\t\tafter pusht, stack is <$(stack_status)>"

        echo "now modify..."
        shopt -o -s allexport  # off --> on
        shopt -o -u hashall     # on --> off
        shopt -s checkjobs      # off --> on
        shopt -u checkwinsize   # on --> off

        check 2

        popt checkwinsize checkjobs hashall allexport
        echo -e "\t\tafter popt, stack is <$(stack_status)>"

        check 3

    }
    clear
    way1   

测试结果如下:

                check step 1...
set +o allexport
set -o hashall
shopt -u checkjobs
shopt -s checkwinsize
                after pusht, stack is <[0]=(off) [1]=(on) [2]=(off) [3]=(on)>
now modify...
                check step 2...
set -o allexport
set +o hashall
shopt -s checkjobs
shopt -u checkwinsize
                after popt, stack is <>
                check step 3...
set +o allexport
set -o hashall
shopt -u checkjobs
shopt -s checkwinsize

下面测试 pushee / popee:

    # 根据实际文件位置修改路径
    source ./stack.sh
    source ./options    
    way2(){
        echo "way2 function start..."
        pushee
        set +e
        local cmd="no-such-command"
        
        echo "执行未知命令将异常, 但由于 set +e 临时取消 "错误立即退出的行为" "
        $cmd

        echo "way2 funciton continue."
        popee
        echo "使用 popee 恢复 errexit 后,再次执行未知命令,将立即退出。
            说明 popee 生效, "
        $cmd
        echo "这句由于最初设置的 set -e 已经恢复, 而不会运行."
    }
    clear 
    set -e
    way2

结果如下:

way2 function start...
执行未知命令将异常, 但由于 set +e 临时取消 错误立即退出的行为
/mnt/d/Estate/asset/OS/linux/application/blogging/lib/unit_test.sh: line 671: no-such-command: command not found
way2 funciton continue.
使用 popee 恢复 errexit 后,再次执行未知命令,将立即退出。
            说明 popee 生效,
/mnt/d/Estate/asset/OS/linux/application/blogging/lib/unit_test.sh: line 677: no-such-command: command not found

成功.