如何突破源代码Bash脚本';s函数

如何突破源代码Bash脚本';s函数,bash,return,exit,terminate,Bash,Return,Exit,Terminate,我有一个源代码的Bash脚本。当该脚本获得源代码时,它将在Bash脚本中运行一个函数。如果符合特定条件,此函数应终止脚本。如何在不终止脚本来源的shell的情况下实现这一点 需要明确的是:我希望终止操作由源代码shell脚本中的函数完成,而不是在源代码shell脚本的主体中完成。我看到的问题是return只是从函数返回到脚本的main,而exit 1终止了调用shell 以下示例说明了该问题: main(){ echo "starting test of environment..."

我有一个源代码的Bash脚本。当该脚本获得源代码时,它将在Bash脚本中运行一个函数。如果符合特定条件,此函数应终止脚本。如何在不终止脚本来源的shell的情况下实现这一点

需要明确的是:我希望终止操作由源代码shell脚本中的函数完成,而不是在源代码shell脚本的主体中完成。我看到的问题是
return
只是从函数返回到脚本的main,而
exit 1
终止了调用shell

以下示例说明了该问题:

main(){
    echo "starting test of environment..."
    ensure_environment
    echo "environment safe -- starting other procedures..."
}

ensure_environment(){
    if [ 1 == 1 ]; then
        echo "environment problemm -- terminating..."
        # exit 1 # <-- terminates calling shell
        return   # <-- returns only from function, not from sourced script
    fi
}

main
main(){
echo“开始环境测试…”
确保环境
echo“环境安全--启动其他程序…”
}
确保环境(){
如果[1==1];则
echo“环境问题--终止…”

#退出1#您可以
从源shell脚本返回

因此,虽然您不能直接从函数中
返回
以获得所需内容,但如果您的函数返回非零值(或其他约定值),则可以从脚本主体中返回

例如:

$ cat foo.sh
f() {
    echo in f "$@"
}

e() {
    return 2
}

f 1
e
f 2
if ! e; then
    return
fi
f 3
$ . foo.sh
in f 1
in f 2

这样如何:通过一个简单的包装器调用一切,这里是“ocall”,它维护一个全局状态,这里是“STILL\u OK”

仍然_OK=true
ocall(){
如果美元还可以
然后
echo--“$@”#这是用于调试的,您可以删除此行
如果“$@”
然后
真的
其他的
仍然_OK=false
fi
fi
}
main(){
ocall echo“开始环境测试…”
o确保良好的环境
ocall echo“环境安全--启动其他过程…”
}
确保环境(){
如果[1==1];则
ocall echo“环境问题--终止…”
#退出1#这是不可能的

如果你源代码一个脚本,它(对于这里涉及的方面)就像在调用(源代码)shell中一行一行地输入每一行。你想留下一个不存在的作用域(源代码脚本),因此不能留下它

我能想到的唯一方法是将退出愿望传递回调用函数并检查它:

main() {
    echo "starting test of environment..."
    [ "$(ensure_environment)" = "bailout" ] && return
    echo "environment safe -- starting other procedures..."
}

ensure_environment() {
    if [ 1 == 1 ]; then
        echo "bailout"
        return
    fi
}

main
您所要求的在其他语言中通常也是不可能的。通常每个函数只能终止自身(通过返回),而不能终止自身以外的更广泛的定义范围(如它所在的脚本)。使用try/catch或类似方法


也可以这样考虑:如果你源脚本,shell函数在源码shell中就知道了,所以你可以稍后再调用它们。没有函数可以终止的周围范围。

这是一个如何通过方法实现目标的方法。我不会为您编写代码,只描述如何完成

您的目标是在当前bash shell中设置/更改环境变量,方法是有效地寻源一个可能复杂的shell脚本。此脚本的某些组件可能会决定停止执行此寻源脚本。使此复杂化的是,此决定不一定是顶级的,但可能位于嵌套函数i中nvocation.
返回
,则没有帮助,
退出
将终止不需要的寻源外壳程序

您的这句话使您的任务变得更容易:

额外的复杂性,我真的不能包含在一个最小的例子 将终止程序集中在一个 功能

这就是你如何做到的:

您不需要寻找真正的脚本来决定将哪个环境设置为什么(“
realscript.bash
”),而是寻找另一个脚本“
ipcscript.bash

ipcscript.bash
将设置一些进程间通信。这可能是使用exec打开的额外文件描述符上的管道,可能是临时文件,也可能是其他文件

ipcscript.bash
然后将作为子进程启动
realscript.bash
。这意味着,环境的变化
realscript.bash
首先只会影响bash的子进程实例的环境。作为子进程启动
realscript.bash
,您可以终止exec在任何嵌套级别上使用exit,而不终止寻源外壳

当您写入时,您对退出的调用将在一个集中式函数中有效,该函数在决定终止执行时从任何级别调用。您的终止函数现在需要在退出之前,以适当的格式将当前环境写入IPC机制


ipcscript.bash
将从IPC机制读取环境设置,并在外壳程序寻源的过程中复制所有设置。

有时我编写一个脚本,其中包含我希望在脚本外部使用的方便功能。在这种情况下,如果脚本运行,那么它会完成它的任务。但是如果脚本是寻源的,它只会将一些函数加载到源shell中。 我使用以下表格:

#!/bin/bash

# This function will be sourcable
foo() {
  echo hello world
}

# end if being sourced
if [[ $0 == bash ]]; then
  return
fi

# the rest of the script goes here

这是可能的

像在任何编程语言中一样执行此操作,并“引发异常”,这将在调用链中传播:

# cat r

set -u

err=

inner () {
   # we want to bailaout at this point:
   # so we cause -u to kick in:
   err="reason: some problem in 'inner' function"
   i=$error_occurred
   echo "will not be called"
}

inner1 () {
   echo before_inner
   inner
   echo "will not be called"
}


main () {
   echo before_inner1
   inner1
   echo "will not be called"
}

echo before_func
main || echo "even this is not shown"

# this *will* be called now, like typing next statement on the terminal:
echo after_main
echo "${err:-}" # if we failed
测试:


您可以通过
2>/dev/null
消除错误,清除这是我喜欢的解决方案(它附带副作用,解释如下):

输出:

# source source_global_trap.sh
RETURNED ONE
IN TRAP
IN TRAP
说明: 简而言之,代码为
ERR
设置
trap
,但在
trap
(作为第一条指令)内,根据
自定义错误代码检查返回代码,并从源脚本返回值为
自定义错误代码
(在这种情况下,任意选择为
13
)。这意味着在任何地方返回
自定义错误\u code
(由于
shopt-s extdebug
,否则只返回第一个
# echo $$
9655
# . r  || true
before_func
before_inner1
before_inner
bash: error_occurred: unbound variable
after_main
reason: some problem in 'inner' function
# echo $$
9655
#!/usr/bin/env bash
# force inheritance of ERR trap inside functions and subshells
shopt -s extdebug
# pick custom error code to force script end
CUSTOM_ERROR_CODE=13

# clear ERR trap and set a new one
trap - ERR
trap '[[ $? == "$CUSTOM_ERROR_CODE" ]] && echo "IN TRAP" && return $CUSTOM_ERROR_CODE 2>/dev/null;' ERR

# example function that triggers the trap, but does not end the script
function RETURN_ONE() { return 1; }
RETURN_ONE
echo "RETURNED ONE"

# example function that triggers the trap and ends the script
function RETURN_CUSTOM_ERROR_CODE() { return "$CUSTOM_ERROR_CODE"; }
# example function that indirectly calls the above function and returns success (0) after
function INDIRECT_RETURN_CUSTOM_ERROR_CODE() { RETURN_CUSTOM_ERROR_CODE; return 0; }
INDIRECT_RETURN_CUSTOM_ERROR_CODE
echo "RETURNED CUSTOM ERROR CODE"

# clear traps
trap - ERR
# disable inheritance of ERR trap inside functions and subshells
shopt -u extdebug
# source source_global_trap.sh
RETURNED ONE
IN TRAP
IN TRAP
# exec bash

# source source_global_trap.sh
RETURNED ONE
IN TRAP
IN TRAP

# source source_global_trap.sh
RETURNED ONE
IN TRAP
IN TRAP
IN TRAP

# source source_global_trap.sh
RETURNED ONE
IN TRAP
IN TRAP
IN TRAP