为什么在源脚本中“exec non-existent-file”会退出 shell?

为什么在源脚本中“exec non-existent-file”会退出 shell?

文档指出,如果当前 shell 是非交互式的并且未设置选项,exec则在出现错误时它将退出 shell 。execfail

我注意到交互式 bash shell 中有一个奇怪的行为,但我无法用文档来解释。此命令

exec non-existent-file

生成错误消息,并且 shell 保持活动状态,正如预期的那样。但如果我将相同的命令放入一个文件中,并且source该文件,则当前 shell 将因失败的 exec 而退出。

有人能帮助我理解为什么会发生这种情况吗?

答案1

我觉得这是一个 bug。下面是某种“侦探故事”。

是的,在exec.def代码中我们看到:

if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
  exit_shell (exit_value);

因此,exec会导致退出 shell a) 在非交互式 shell 中使用false“no_exit_on_failed_exec” b)exec在子 shell 中运行时

对于交互式 shell,如果命令存在,则 shell 将被调用的命令替换:

执行

此 shell 内置命令用指定命令替换当前进程。通常,当 shell 遇到命令时,它会派生一个子进程来实际执行该命令。 使用内置的 exec 时,shell 不会 fork,并且 exec'ed 命令会替换 shell。因此,在脚本中使用时,当执行的命令终止时,它会强制退出脚本

例子 15-24. exec 的效果

#!/bin/bash
exec echo "Exiting \"$0\" at line $LINENO."   # Exit from script here.
# $LINENO is an internal Bash variable set to the line number it's on.
**# The following lines never execute.**

http://www.tldp.org/LDP/abs/html/internal.html#EXECREF

source命令没有调用子shell- 强制执行所有命令在你当前活动的 shell 中(例如,它用于设置变量 - 不是用于将要退出的某些子 shell,而是用于当前 shell)。因此,source在 shell 中直接执行命令后,shell 将终止(这是预期的行为)。

当使用“source”运行脚本时,它会在现有的 shell 中运行,脚本创建或修改的任何变量在脚本完成后仍可用。 http://ss64.com/bash/source.html

我已经编译bash-4.2并在调试器中运行它gdb

这些是退出前执行的最后命令:

(gdb) 
bash: exec: non-existing-file: не найден
163       exit_value = EX_NOTFOUND; /* As per Posix.2, 3.14.6 */
(gdb) 
165       goto failed_exec;
(gdb) 
235   FREE (command);
(gdb) 
237   if (subshell_environment || (interactive == 0 && no_exit_on_failed_exec == 0))
(gdb)
238     exit_shell (exit_value);
(gdb)
[Inferior 1 (process 4034) exited with code 0177]

打印变量:

(gdb) p subshell_environment 
$1 = 0
(gdb) p interactive
$2 = 0
(gdb) p no_exit_on_failed_exec 
$3 = 0

事实证明,interactive=0在获取内置的 时,shell 是非交互式的( )exec。这就是这种行为的原因。它与记录的行为相矛盾,因此,有人可能会说,你发现了一个错误。

此处将更改为non-interactiveinteractive=0evalfile.c):

interactive:
Old value = 1
New value = 0
_evalfile (filename=0xa014c8 "exec1.sh", flags=14)
at evalfile.c:226
223  if (flags & FEVAL_NONINT)
224    interactive = 0;

由于flags=14. flags被设置了一个更高的级别,在函数source_file( evalfile.c) 中:

#1  0x0000000000485dcf in source_file (filename=0x9fb8c8 "exec1.sh", sflags=0)

338  flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;
339  if (sflags)
340    flags |= FEVAL_NOPUSHARGS;
341  /* POSIX shells exit if non-interactive and file error. */
342  if (posixly_correct && interactive_shell == 0 && executing_command_builtin == 0)
343    flags |= FEVAL_LONGJMP;
344  rval = _evalfile (filename, flags);

定义如下:

#define FEVAL_BUILTIN       0x002
#define FEVAL_UNWINDPROT    0x004
#define FEVAL_NONINT        0x008

-> 标志 1110 =14。

因此,据我了解,这是一个错误:source在当前 shell 中执行命令,但设置标志FEVAL_NONINT 0x008- 非交互式(此处有错误):, evalfile.csource_file

338 flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

我在错误跟踪器上创建了一个问题:

http://savannah.gnu.org/support/index.php?108980

會看的。


编辑1: 正如 bash 支持者在票上评论的那样

“当将文件参数读取到源内置命令 (interactive == 0) 时,shell 当前不是交互式的,尽管 shell 本身是交互式的 (interactive_shell == 1)。"

而且正如他所说,这种行为在近期可能不会发生改变。

现在这个问题似乎已经结束了。

相关内容