退出函数堆栈而不退出 shell

46

我在写一个脚本时遇到了一个奇怪的问题。如果我引用了一个包含许多可能调用错误函数并输出字符串后退出的函数的脚本,它会退出我的shell。我知道为什么会这样。因为函数调用与调用者在同一进程空间中(至少在bash中是这样),所以函数内的退出将使用提供的退出码终止当前进程。例如:

error()
{
  echo $1
  exit 1
}

fn()
{
  if [ $# == 0 ]; then
    error "Insufficient parameters."
  fi
  # do stuff
}

$ fn
Insufficient parameters.
[shell terminates]

我的问题是,我能否在不终止当前 shell 和不创建新的子 shell 的情况下退出函数堆栈中的所有函数吗?

谢谢

6个回答

46

要在不退出shell的情况下退出函数堆栈,可以使用以下命令:

kill -INT $$

正如pizza所述,这就像按下Ctrl-C一样,会停止当前的脚本运行并将您带回到命令提示符。

 

 


注意:我没有选择pizza的答案的唯一原因是因为这个答案被埋在他/她的答案中,并没有直接回答。


3
以下的“return”似乎更可取:这是为此目的内置到shell中的功能。杀死shell是不优雅的。 - JPGConnolly
4
@JPGConnolly 你说的“以下的‘return’似乎更可取”是什么意思?如果你的意思是使用内置的return语句,那么你没有正确理解问题,因为没有简单的方法可以在一个被多个函数调用的函数中使用它,使它能够跳回到基本shell,除非必须询问每个函数调用的返回值,在某些情况下这是不切实际或不可能的。在这种情况下,执行kill -INT $$比使用return要优雅得多。 - Adrian
调用源函数的$?值是多少?或者它也会终止吗? - Michael
@Michael,$$的值是shell进程的pid。 - Adrian
1
@JPGConnolly...据我理解,我们想要从一个源脚本中BREAK,而不退出shell。这绝对是我在这里的原因。这是为了一个被放弃的“逃逸”源代码。请原谅我指出,在Windows(批处理)shell中有这个功能,即exit /b - 仅退出脚本,而不退出shell(窗口)。 - will
@JPGConnolly是对的。良好的做法是使用关键字return来结束函数的执行。它在各种情况下都能起作用,例如:http://tpcg.io/_Z82DJH - undefined

16

你可以进行以下操作:

exit() { return $1;}
然后。
source ./your_script 

回应怀疑者,这只对当前的shell生效,不影响您生成的其他shells。

更详细的形式可以是

exit() {
    local ans
    local line
    read -p "You really want to exit this? " line
    ans=$(echo $line)
    case "$ans" in
            Y);;
            y);;
            *)kill -INT $$;;
    esac
    unset -f exit
    exit $1
}

这样做不行。它只会导致出错的函数被返回,而不是我想要的结果。 - Adrian
如果你不想返回它,你可以用“kill -INT $$”代替“return $1”,这会将其带回到你当前的shell提示符中。 - pizza
这也行不通,因为它应该在当前 shell 中运行,而那样会终止它。 - Adrian
1
就像按下Control-C一样,它不会终止当前的shell,并且会解开所有当前的函数并回到提示符。 - pizza
2
这是一个有用的技巧,可避免不触及现有代码而退出。然而,如果代码要在用户当前的shell上下文中运行并且可以被修改,我建议使用 kill -INT $$; - TrinitronX
显示剩余2条评论

7
你需要在每个函数中添加 `return` 语句,以检查它们依次调用的任何函数的返回值。源文件的导入类似于将代码剪切并粘贴到当前上下文中,只有变量(如 `$BASH_SOURCE`)稍有不同。
或者,你可以将 `fn` 定义为 shell 脚本,这样 `exit` 就会按照你的意愿执行(除非进程分支开销太大)。

1
检查所有函数的退出值是没有用的。目前,为了解决这个问题,我让shell脚本调用其中的各个函数。这样退出函数不会退出我的shell。 - Adrian
你不需要检查所有的退出值 - 只需检查它是否成功 - if my_function - 或者退出代码是否为非零 - my_function; if [ $? -ne 0 ] - l0b0
将函数定义为shell脚本是一个好的解决方案,但在该函数中使用"exit"将退出shell。您在使用"return"来停止函数而不退出shell方面是正确的。 - JPGConnolly

2

使用return语句, 但是在调用错误后需要添加return


1
函数可以在没有返回语句的情况下返回。 - Dennis Williamson
2
但是 OP 希望该函数立即终止,而不是在到达函数体底部时终止。 - Mark Reed
我希望堆栈上的所有函数都终止。“但在 if 之前;在做事之前”是什么意思? - Adrian
我在回答Dennis和Adrian,为什么不在一个新函数中生成一个新的shell呢?我认为这是最简单的解决方案:fnc() (fn),或者更易读的方式是fnc() { (fn)}。 - Nahuel Fouilleul
1
如果调用是函数的最后一个语句,则状态会隐式返回,否则可以使用特殊变量$?。 - Nahuel Fouilleul
显示剩余2条评论

2

Shell并没有真正的异常机制,无法一次性回溯多个函数调用。您只能检查返回值,并手动一直返回。


我正在检查下降的方式。只想退出当前正在运行的所有函数,而不退出shell。 - Adrian
1
这意味着你要使用 return 而不是 exit。但是我所说的“一路下去”是指你必须在每个函数内部都单独使用 return,而不仅仅是在最内层函数中使用一次。 - Mark Reed
1
很不幸,这对我想做的事情没有用处。 - Adrian

0
将脚本嵌套在shell中:
#!/bin/bash
(
function foo { echo exiting; exit 1; }
foo
)

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