我有一个源代码的Bash脚本。当这个脚本来源时,它在Bash脚本中运行一个函数。如果匹配某个条件,此函数应终止脚本。如何在不终止脚本所在的shell的情况下完成此操作?
要明确:我希望终止操作由源shell脚本中的函数完成,而不是在源shell脚本的主体中完成。我可以看到的问题是return
只是从函数返回到脚本的主要部分,而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
答案 0 :(得分:8)
您可以从源shell脚本中return
。 POSIX spec
因此,虽然您可以直接从函数中return
得到您想要的内容,但如果您的函数返回非零,则可以从脚本主体返回(或其他一些商定的价值)。
例如:
$ 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
答案 1 :(得分:2)
这个怎么样:通过一个简单的包装器调用一切,这里&#34; ocall&#34;,它维持一个全局状态,这里&#34; STILL_OK&#34;
STILL_OK=true
ocall() {
if $STILL_OK
then
echo -- "$@" # this is for debugging, you can delete this line
if "$@"
then
true
else
STILL_OK=false
fi
fi
}
main(){
ocall echo "starting test of environment..."
ocall ensure_environment
ocall echo "environment safe -- starting other procedures..."
}
ensure_environment(){
if [ 1 == 1 ]; then
ocall echo "environment problemm -- terminating..."
# exit 1 # <-- terminates calling shell
return 1 # <-- returns from sourced script but leaves sourcing shell running
fi
}
ocall main
答案 2 :(得分:2)
这是不可能的。
如果您提供脚本,那么(对于此处涉及的方面)就像在调用(sourcing)shell中逐个输入每一行一样。您想要保留一个不存在的范围(源脚本),因此不能将其保留。
我能想到的唯一方法是将exit-wish传递回调用函数并检查它:
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
您要求的内容通常也不可能用于其他语言。通常,每个函数只能自行终止(通过返回),而不是自身之外的更宽的定义范围(就像它所驻留的脚本一样)。 An exception to this rule is exception handling使用try / catch或类似内容。
还要考虑这一点:如果您获取此脚本,那么shell函数在源代码shell中已知。所以你可以稍后再打电话给他们。然后(再次)没有函数可以终止的周围范围。
答案 3 :(得分:2)
这是一个如何使用您的方法实现目标的方法。我不会为你编写代码,只是描述它是如何完成的。
您的目标是通过有效地获取可能复杂的shell脚本来设置/更改当前bash shell中的环境变量。此脚本的某些组件可能决定应停止执行此源脚本。使这个复杂化的原因是这个决定不一定是顶级的,但可能位于嵌套的函数调用中。 return
然后,没有帮助,exit
会终止采购外壳,这是不可取的。
您的任务通过以下声明变得更容易:
我可以在一个最小的例子中真正包含的额外复杂性 非常希望将终止程序集中在一个 功能
您就是这样做的:
您可以使用另一个脚本&#34; realscript.bash
&#34;而不是寻找决定将哪个环境设置为什么(&#34; ipcscript.bash
&#34;)的真实脚本。
ipcscript.bash
将设置一些进程间通信。这可能是您使用exec打开的一些额外文件描述符上的管道,它可能是一个临时文件,它可能是其他内容。
ipcscript.bash
将realscript.bash
作为子进程启动。这意味着,realscript.bash
首先执行的环境更改只会影响bash子进程实例的环境。将realscript.bash
作为子进程启动,您可以使用exit终止任何嵌套级别的执行,而不会终止源代码shell。
当您编写时,您的退出调用将在一个集中函数中生效,该函数在决定终止执行时从任何级别调用。在退出之前,您的终止功能现在需要以适当的格式将当前环境写入IPC机制。
ipcscript.bash
将从IPC机制中读取环境设置,并在源代码shell的过程中重现所有设置。
答案 4 :(得分:2)
这是我更喜欢的解决方案(它有副作用,解释如下):
#!/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
说明:
简而言之,该代码为 trap
设置了一个 ERR
,但是,在 trap
内部(作为第一条指令)根据 CUSTOM_ERROR_CODE
检查返回代码并从源脚本返回仅适用于并具有 CUSTOM_ERROR_CODE
的值(在本例中任意选择为 13
)。这意味着在任何地方返回 CUSTOM_ERROR_CODE
(由于 shopt -s extdebug
,否则只是第一级函数/命令)应该产生结束脚本的预期结果。
副作用:
[01] CUSTOM_ERROR_CODE
中的错误代码可能被脚本控制之外的命令使用,因此可以在没有明确指示的情况下强制脚本结束。这应该很容易避免,但可能会引起一些不适。
[02] 调用 shopt -s extdebug
可能会导致不需要的行为,具体取决于脚本中的其他因素。详情请见:https://www.gnu.org/software/bash/manual/html_node/The-Shopt-Builtin.html
[03] 更重要的是,这是在干净的环境中获取脚本的输出,三遍,一个接一个:
# 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
关于为什么会发生这种(额外的 trap
调用),我有几种理论,但没有结论性的解释。它在我的测试期间没有造成麻烦,但强烈建议您进行任何澄清。
答案 5 :(得分:1)
有时我会编写一个脚本,它具有我想在脚本之外使用的便捷功能。在这种情况下,如果脚本运行,那么它就可以了。但是如果脚本是源代码的,它只是将一些函数加载到源代码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
答案 6 :(得分:0)
是可能。
像在任何编程语言中一样进行操作,并“引发异常”,它将在调用链中传播:
# 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
测试:
# 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
您可以通过2>/dev/null
消除错误,清除