![为什么在源脚本中“exec non-existent-file”会退出 shell?](https://linux22.com/image/1471220/%E4%B8%BA%E4%BB%80%E4%B9%88%E5%9C%A8%E6%BA%90%E8%84%9A%E6%9C%AC%E4%B8%AD%E2%80%9Cexec%20non-existent-file%E2%80%9D%E4%BC%9A%E9%80%80%E5%87%BA%20shell%EF%BC%9F.png)
文档指出,如果当前 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.**
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-interactive
,interactive=0
(evalfile.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.c
在source_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)。"
而且正如他所说,这种行为在近期可能不会发生改变。
现在这个问题似乎已经结束了。