2015-11-24 27 views
5

我有一个Bash脚本来源。当这个脚本来源时,它在Bash脚本中运行一个函数。如果某个条件匹配,该函数应该终止脚本。如何在不终止脚本所在的shell的情况下完成此任务?如何摆脱源Bash脚本的功能

要清楚:我希望终止操作由源脚本中的函数完成,而不是在源脚本的主体中完成。我可以看到的问题是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 
+1

改为执行脚本。这样,'exit'将退出它正在运行的子shell。 – fedorqui

+1

有趣。你可以举一个小例子来开始,那可以改进。例如。一个终止shell。 –

+0

你问一些不可能的事情。你必须重构你的代码。 –

回答

2

这是一个配方,你如何用你的方法实现你的目标。我不会为你写代码,只是描述它是如何完成的。

您的目标是通过有效地获取可能复杂的shell脚本来设置/更改当前bash shell中的环境变量。此脚本的某些组件可能会决定停止执行此源脚本。使这变得复杂的是这个决定不一定是顶级的,但可能位于嵌套函数调用中。 return,然后,没有帮助,并且exit将终止源shell,这是不希望的。

你的任务是通过你的这种说法变得更加容易:

额外的复杂性,我真的不能包括一个小例子 使得它非常希望集中在 功能的终止程序。

这是你如何做到这一点:

而不是你的采购是决定设置什么(“realscript.bash‘),则源的另一剧本’ipcscript.bash”的环境中的实际脚本。

ipcscript.bash将设置一些进程间通信。这可能是一些你用exec打开的额外文件描述符的管道,它可能是一个临时文件,它可能是别的。

ipcscript.bash将作为子进程启动realscript.bash。这意味着,realscript.bash首先会影响该子进程实例的环境变化。作为一个子进程启动realscript.bash,您可以在任何嵌套级别退出而不终止源代码的情况下终止执行。

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

ipcscript.bash将从IPC机制中读取环境设置并重现sourcing shell过程中的所有设置。

7

您可以return从采购的shell脚本。 POSIX spec

所以,虽然你可以从函数不return直接得到你想要的,你什么可以从脚本的主体回报,如果你的函数返回非零(或其他一些价值时约定)。

例如:

$ cat foo.sh 
f() { 
    echo in f "[email protected]" 
} 

e() { 
    return 2 
} 

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

感谢您的解决方案。我知道这种方法。我的目的是找到一种终止源函数的方法,因为在一个最小的例子中,我不能真正包含的额外复杂性使得将终止过程集中在一个函数中是非常理想的。 – d3pd

+0

@ d3pd你不行。没有没有子外壳。这是不可能的。你可以在这个函数中保留你想要的任何和所有的逻辑。你只需要在'if'或'ensure_environment‖中调用函数返回“或类似的东西。 ....您可能*能够通过检查'$?'或其他任何变量的'RETURN'陷阱来做些事情。 –

2

如何:通过一个简单的包装,这里的 “ocall”,维护全局状态,这里的 “STILL_OK” 叫一切

STILL_OK=true 

ocall() { 
    if $STILL_OK 
    then 
     echo -- "[email protected]" # this is for debugging, you can delete this line 
     if "[email protected]" 
     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 
+0

为了让'$ @'保留它的属性,它应该基本上总是用双引号。这里并不重要,但对我来说,它立即引发了一个新手报警,所以你可能想要解决它,只是出于这个原因。 – tripleee

+0

没错。我可以使用红宝石时避免使用bash。纠正。 –

+0

Nitpicking:'如果$ STILL_OK'是安全弱点。如果[[$ STILL_OK“=”true“]'更好地使用if',即使它不够优雅。否则,如果您在不设置“STILL_OK”的情况下实现通向此源代码行的任何路径,那么您将执行环境中当前为“STILL_OK”设置的任何路径。也许只有一个人不知道。 – Alfe

2

这是不可能。

如果你输入一个脚本,它就是(对于这里涉及的方面),就像在调用(sourcing)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 

什么你问的也通常是不可能的其他语言。通常情况下,每个函数只能通过返回来终止自己,而不是自身之外更广泛的定义范围(如它驻留的脚本)。 An exception to this rule is exception handling使用try/catch或类似的。

另外考虑这个问题:如果你输入这个脚本,shell函数就会在sourcing shell中知道。所以你可以再打电话给他们。然后再次(没有)周围的作用域可能会终止。

0

有时我写一个脚本,它具有我想在脚本之外使用的方便功能。在这种情况下,如果脚本运行,则它执行它的操作。但是如果脚本来源,它只是将一些函数加载到源代码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