繁体   English   中英

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

[英]How can I create a generic shell command to exit or return from a script that is either executed or sourced?

我正在尝试实现一个命令,该命令假定是Bash附带的常规“退出”和“返回” shell内置程序的包装器(Bourne等),该命令不受这些不兼容问题的困扰如果我使用“ 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命令,即

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

现在,无论脚本是源代码还是执行的脚本,我都将获得5的退出代码。对于错误类型的“ exit”或“ return”,没有错误。

我在getScriptPath命令上遇到麻烦的原因是要处理可能位于符号链接下的文件系统路径上的脚本文件。

之所以在testSourced的复杂性在于,测试需要工作,即使如果来源是由调用脚本这样做,即它需要确保它正在测试一个事实,即本身是测试脚本来源,而不是调用脚本。

现在,我知道执行此操作的正常方法如下:

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

这样做的问题是,我无法弄清楚如何包装它以对其进行参数化,因此我可以将其用作“完成”或“终止”命令,该命令将以此错误级别终止脚本,就像我为它加上别名一样,我可以如果没有返回代码,或者该函数起作用,那么它将返回到调用函数/脚本(如果有)。

必须有一种比我在上面的“完成”实现中想出的方法更便捷的方法! 当然这是标准的事情吗?

有人解决过这个吗? 我是否缺少明显的东西?

只是要确认一下,我想做的是:-单个命令立即终止脚本-如果执行了脚本,则执行相同的命令,源代码-如果在函数,主脚本主体或子函数中使用,则执行相同的命令-执行如果该脚本是直接调用的或由另一个可能执行或源自的脚本调用或提供的,则也是相同的(或可能导致执行该脚本的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

不幸的是,没有一种普遍的观念认为仅从源脚本中退出。 例如,采取以下情况:

  • 脚本a.sh具有方法a1() a2()
  • 脚本b.sh具有方法b1() b2()
  • 现在a1正在呼叫b1本身正在呼叫a2

从a2内退出脚本a.sh是什么意思? 刚离开a2()吗? 还是通过b1()a1()展开调用堆栈?

因此,即使出现别名问题(*),也没有简单的命令可从方法内运行以仅从当前脚本退出。 return将仅从方法返回,而exit将杀死整个外壳。 两者之间没有。

现在,bash通过FUNCNAME数组使调用栈可用,因此应该可以编写一个系统,在其中强制所有方法立即返回(使用DEBUG陷阱处理程序)直到调用栈中所需的位置。 但是我不会将我的项目押在这样的功能上:)

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


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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM