首页 文章

如何创建通用shell命令以退出或从执行或源代码的脚本返回?

提问于
浏览 855
1

我正在尝试实现一个单独的命令,我认为它将是Bash(Bourne等)附带的正常'exit'和'return'shell内置函数的包装器,这个命令不受这些不兼容问题的困扰 . 因为如果我使用'exit 1'来结束错误级别为1的脚本,如果我获取该脚本,它将导致我所在的shell终止 .

同样,如果我使用return,它会遇到以下问题:a)它只返回调用函数,而不是在没有附加逻辑的情况下结束整个脚本运行 . b)如果我在主脚本中使用return并执行它,它将会出错而不是以一个级别终止 .

我提出的最佳解决方案是:

我写的所有shell脚本都以这个序言开头,以获得一些稳定的基本变量:

# Core Functions and Alias definitions Used For Script Setup
getScriptFile () { basename "${BASH_SOURCE[0]}" ; } # This filename as executed
getScriptPath () { local f="${BASH_SOURCE[0]}" ; local p ; while test -L "${f}"
    do p="$( cd -P "$( dirname "${f}" )" && pwd )" ; f="$( readlink "${f}" )" &&
    f="${p}/${f}" ; done ; p="$( cd -P "$( dirname "${f}" )" && pwd )" ;
    printf "%s\n" "${p}" ; }
shopt -s expand_aliases
SCRIPTpath="$( getScriptPath ; )"
SCRIPTfile="$( getScriptFile ; )"

所以,现在我添加它:

testEq () { [ "${1}" = "${2}" ] ; } # True if there is equality
testMatch () { [[ ${1} =~ ${2} ]] ; } # True if ARG1 MATCHES the Extended RegEx in ARG2
testInt () { [ "${1}" -eq "${1}" ] 2>/dev/null ; }  # I know this is okay with bash, to test with bourne
testIntLt () { testInt "${1}" && testInt "${2}" && [ "${1}" -lt "${2}" ] ; }
testSourced () { local c="0" ; local m="${#BASH_SOURCE[@]}" ;
    until testEq "$( basename "${BASH_SOURCE[${c}]}" )" "${SCRIPTfile}" &&
    testMatch "${FUNCNAME[${c}]}" "source|main" &&
    testIntLt "${c}" "${m}" ; do ((c++)) ; done ; 
    if testEq "source" "${FUNCNAME[${c}]}" ; then return 0 ; 
    else return 1 ; fi ; } # True if calling script is sourced into environment
getValidTerminationCommand () { testSourced && printf "%s" "return" || printf "%s" "exit" ; }
alias finish='eval $( getValidTerminationCommand ; )'

......当我想突然完成一个剧本时,我使用了我的新完成命令,即

$ finish 5  <-- to finish with an error level of 5

现在,无论脚本是源还是执行,我都会得到退出代码5.对于错误类型的“退出”或“返回”没有错误 .

我在getScriptPath命令上有复杂性的原因是处理可能存在于符号链接下的文件系统路径上的脚本文件 .

testSourced复杂化的原因是测试需要工作,即使正在测试它的源代码的脚本是由调用脚本完成的,即它需要确保它正在测试它本身就是这样的事实 . 来源,而不是说一个调用脚本 .

现在,我知道这样做的正常方法很简单如下:

$ return 5 2>/dev/null || exit 5  # If I want to end a script with error level 5

问题是我无法弄清楚如何包装它来参数化它,以便我可以将它用作'finish'或'terminate'命令,它将使用此错误级别终止脚本,就好像我别名它,我可以做这没有返回的代码,或者如果它运行它然后它将返回调用函数/脚本,如果它有一个 .

必须有一个便携,简单的方式,而不是我在上面实现'完成'时提出的方法!?!当然这是标准的事情吗?

有人解决过这个吗?我错过了一些明显的东西吗

只是为了确认,我想要做的是: - 单个命令立即终止脚本 - 如果脚本被执行则相同的命令,sourced - 如果在函数中使用相同的命令,主脚本体或子函数 - 表现为如果脚本直接调用或源自可执行或源自的另一个脚本(或可能导致正在运行的脚本的n级执行),则相同 . - 用作Bash shell的标准命令(如果可能,Bourne),以便尽可能移植 .

任何人都有一些他们用于这项工作的工具?请告诉我?

在期待中感谢!?! :)

我以前尝试过的测试脚本:

#!/bin/bash

# Core Functions and Alias definitions Used For Script Setup
getScriptFile () { basename "${BASH_SOURCE[0]}" ; } # This filename as executed
getScriptPath () { local f="${BASH_SOURCE[0]}" ; local p ; while test -L "${f}"
    do p="$( cd -P "$( dirname "${f}" )" && pwd )" ; f="$( readlink "${f}" )" &&
    f="${p}/${f}" ; done ; p="$( cd -P "$( dirname "${f}" )" && pwd )" ;
    printf "%s\n" "${p}" ; }
shopt -s expand_aliases
SCRIPTpath="$( getScriptPath ; )"
SCRIPTfile="$( getScriptFile ; )"
testEq () { [ "${1}" = "${2}" ] ; } # True if there is equality
testMatch () { [[ ${1} =~ ${2} ]] ; } # True if ARG1 MATCHES the Extended RegEx in ARG2
testInt () { [ "${1}" -eq "${1}" ] 2>/dev/null ; }  # I know this is okay with bash, to test with bourne
testIntLt () { testInt "${1}" && testInt "${2}" && [ "${1}" -lt "${2}" ] ; }
testSourced () { local c="0" ; local m="${#BASH_SOURCE[@]}" ; until testEq "$( basename "${BASH_SOURCE[${c}]}" )" "${SCRIPTfile}" && testMatch "${FUNCNAME[${c}]}" "source|main" && testIntLt "${c}" "${m}" ; do ((c++)) ; done ; if testEq "source" "${FUNCNAME[${c}]}" ; then return 0 ; else return 1 ; fi ; } # True if calling script is sourced into environment
getValidTerminationCommand () { testSourced && printf "%s" "return" || printf "%s" "exit" ; }
alias finish='eval $( getValidTerminationCommand ; )'

anotherTestFunc ()
{
    echo "anotherTestFunc ()"
    finish 10
    echo "anotherTestFunc () finish"
}

testFunc ()
{
    echo "testFunc ()"
    anotherTestFunc
    echo "testFunc () finish"
}

echo test start
testFunc
echo more outside text

2 回答

  • 1

    不幸的是,没有普遍的概念只从源自的脚本中退出;例如,采取以下方案:

    • 脚本 a.sh 有方法 a1() a2()

    • 脚本 b.sh 有方法 b1() b2()

    • 现在 a1 正在调用 b1 ,它本身正在调用 a2

    从a2中,从脚本a.sh退出的是什么意思?刚离开 a2() ?或者通过 b1()a1() 解除调用堆栈?

    因此,即使存在别名问题(*),也没有简单的命令可以在方法中运行以仅从当前脚本退出 . return 将仅从方法返回, exit 将终止整个shell . 介于两者之间 .

    现在,bash通过FUNCNAME数组使调用堆栈可用,因此应该可以编写一个系统来强制所有方法立即返回(使用DEBUG陷阱处理程序)直到调用堆栈中所需的点 . 但我不打赌我的项目有这样的功能:)

    或许更好的解决方案是通过在某些时候显式启动子shell来引入一些"exit boundaries" . 在上面的示例中,假设 a1 从子shell中调用 b1 ,则 a2 中的退出将自动回退到 a1 . 但是,如果您需要从调用的方法中修改shell状态(例如变量),这将无济于事 .


    (*)恕我直言,你的解决别名限制的方法是非常聪明的

  • 0

    bash-exit-script-from-inside-a-function与您的测试示例相结合,对于两种情况都返回10:

    #!/bin/bash
    
    returnBreak () { SCRIPTresult=${1:-0} ; break 10000 ; }
    setExitCode () { local s=$? ; [[ -z $SCRIPTresult ]] && return $s ; return $SCRIPTresult ; }
    
    anotherTestFunc ()
    {
        echo "anotherTestFunc ()"
        returnBreak 10
        echo "anotherTestFunc () finish"
    }
    
    testFunc ()
    {
        echo "testFunc ()"
        anotherTestFunc
        echo "testFunc () finish"
    }
    
    SCRIPTresult=""
    for null in null;
    do
        echo test start
        testFunc
        echo more outside text
    
    done
    setExitCode
    

相关问题