从函数内部退出 bash 脚本

7

有些情况下,您想要从函数内部终止脚本:

function die_if_fatal(){
    ....
    [ fatal ] && <termination statement>
}

如果脚本是通过“$ . script”命令引入的,并且终止语句为:
  • 使用“return”会从die返回,但不会结束脚本。
  • 使用“exit”会终止会话(不返回脚本)。
如果脚本是通过“chmod +x script; ./script”命令执行的:
  • 使用“return”会从die返回,但不会结束脚本。
  • 使用“exit”不会返回到die并终止脚本。
一种简单的方法是使用返回代码并在返回时检查它们,但是我需要停止父级,而不修改调用脚本。
有其他方法可处理此问题,但是想象一下如果嵌套了五层复杂脚本,而您发现必须结束脚本,也许可以使用“魔术”退出代码?我只想在引入代码时执行这个操作。
我正在寻找一种简单的语句来结束正在运行的被引入的脚本。
在引入脚本时,从函数内部结束脚本应该使用什么方法?

2
我认为这很困难。请查看http://stackoverflow.com/questions/33303605/how-can-i-create-a-generic-shell-command-to-exit-or-return-from-a-script-that-is,但是那里没有有用的答案。 - Barmar
调用者脚本是否被引用?在子shell中执行调用者脚本,并引用您的脚本。退出应该终止带有调用者脚本的子shell,而不终止会话。 - Walter A
3个回答

2
假设您的脚本不会在循环内部被调用,您可以将脚本的主体放入一个人工的只运行一次的循环中,并使用break命令退出脚本。
为了规范化这个想法并提供一些支持工具,您的脚本必须具有以下结构:
#!/bin/bash

my_exit_code=''
bailout() {
    my_exit_code=${1:-0}

    # hopefully there will be less than 10000 enclosing loops
    break 10000
}

set_exit_code() {
    local s=$?
    if [[ -z $my_exit_code ]]
    then
        return $s
    fi
    return $my_exit_code
}

###### functions #######

# Define your functions here.
#
# Finish the script from inside a function by calling 'bailout [exit_code]'

#### end functions #####

for dummy in once;
do

    # main body of the script
    # 
    # finish the script by calling 'bailout [exit_code]'

done
set_exit_code

1
脚本已经通过$$知道了它的pid。它可以轻松地自杀。
kill -SIGPIPE "$$"  # Die with exit code 141

1

我的想法是基于PGID(进程组标识符)和SID(会话标识符)。除非您直接从会话领导者获取脚本,否则以下解决方案适用。另一方面,会话领导者大多是守护进程和交互式 shell。您可以通过ps aux | awk '$8 ~ /s/ { print }'验证哪些进程作为会话领导者运行。

来源

/tmp/my_quit.sh:

get_pid()
{
    pgid=$( ps -q $$ -o pgid= )
    sid=$( ps -q $$ -o sid= )
    if [[ $pgid == $sid ]]; then
        echo 0
    else
        echo $pgid
    fi
}

fatal()
{
    echo "1"
}

die_if_fatal()
{
    if [ $(fatal) -ne 0 ]; then
        pid=$( get_pid )
        if [ $pid -ne 0 ]; then
            echo "      >> Kill $pid"
            kill $pid
        else
            return
        fi
    fi
    echo "Rest of die_if_fatal's logic"
}

die_if_fatal
echo "      >> Sourced from a session leader. Will not end the process."

/tmp/pack1.sh:

echo "$0: PID: $$ PGID: $( ps -q $$ -o pgid= ) SID=$( ps -q $$ -o sid= )"
echo "  >> Sourcing my_quit..."
. /tmp/my_quit.sh
echo "  >> Executing my_quit..."
/tmp/my_quit.sh

/tmp/pack2.sh:

echo "$0: PID: $$ PGID: $( ps -q $$ -o pgid= ) SID=$( ps -q $$ -o sid= )"
echo "Sourcing pack1..."
. /tmp/pack1.sh
echo "Executing pack1"
/tmp/pack1.sh

使用案例

直接从Shell中执行die_if_fatal (my_quit.sh) - 没有上面的脚本

执行脚本:

[kan@pckan ~]$ /tmp/my_quit.sh 
      >> Kill 11360
Finished
[kan@pckan ~]$

获取脚本:

[kan@pckan ~]$ . /tmp/my_quit.sh 
      >> Sourced from a session leader. Will not end the process.
[kan@pckan ~]$ 

pack1.sh执行 - 嵌套1级

从shell执行pack1.sh:

[kan@pckan ~]$ /tmp/pack1.sh 
/tmp/pack1.sh: PID: 11260 PGID: 11260 SID= 1630
  >> Sourcing my_quit...
      >> Kill 11260
Finished
[kan@pckan ~]$

从shell中获取pack1.sh文件:
[kan@pckan ~]$ . /tmp/pack1.sh 
/bin/bash: PID: 1630 PGID:  1630 SID= 1630
  >> Sourcing my_quit...
      >> Sourced from a session leader. Will not end the process.
  >> Executing my_quit...
      >> Kill 11316
Finished
[kan@pckan ~]$

pack2.sh执行 - 两个(可能更多)嵌套级别

从shell执行pack2.sh:

[kan@pckan ~]$ /tmp/pack2.sh
/tmp/pack2.sh: PID: 11535 PGID: 11535 SID= 1630
Sourcing pack1...
/tmp/pack2.sh: PID: 11535 PGID: 11535 SID= 1630
  >> Sourcing my_quit...
      >> Kill 11535
Finished
[kan@pckan ~]$ 

从shell中获取pack2.sh的来源:
[kan@pckan ~]$ . /tmp/pack2.sh
/bin/bash: PID: 1630 PGID:  1630 SID= 1630
Sourcing pack1...
/bin/bash: PID: 1630 PGID:  1630 SID= 1630
  >> Sourcing my_quit...
      >> Sourced from a session leader. Will not end the process.
  >> Executing my_quit...
      >> Kill 11618
Finished
Executing pack1
/tmp/pack1.sh: PID: 11627 PGID: 11627 SID= 1630
  >> Sourcing my_quit...
      >> Kill 11627
Finished
[kan@pckan ~]$ 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接