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

时间:2015-10-23 13:30:50

标签: linux bash shell unix sh

我正在尝试实现一个单独的命令,我假设它将是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'命令,该命令将使用此错误级别终止脚本,就像我将其作为别名一样,我可以在没有返回代码的情况下执行此操作,或者如果它正常运行,那么它将返回调用函数/脚本(如果有)。

我必须有一个便携,简单的方式来实现上面的“完成”!?!当然这是标准的事情吗?

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

只是为了确认,我想要做的是: - 单个命令立即终止脚本 - 如果执行脚本,则执行相同的命令 - 如果在函数,主脚本体或子函数中使用,则使用相同的命令 - 如果直接调用脚本或直接或通过可执行或源自的其他脚本(或可能导致正在运行的脚本的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 个答案:

答案 0 :(得分: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来引入一些“退出边界”。在上面的示例中,假设a1在子shell中调用b1,则a2中的退出将自动回退到a1。但是,如果您需要从调用的方法中修改shell状态(例如变量),这将无济于事。

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

答案 1 :(得分: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