Чому "exec неіснуючий файл" виходить з оболонки, коли в скрипті, який розміщено?


6

У документації на execзазначається, що вона вийде з оболонки у разі помилки, якщо поточна оболонка не інтерактивна і execfailопція не встановлена.

Я помітив дивну поведінку в інтерактивній оболонці bash, яку я не можу пояснити з документами. Ця команда

exec non-existent-file

видає повідомлення про помилку, і оболонка залишається активною, як очікувалося. Але якщо я вкладу таку ж команду у файл і sourceцей файл, поточна оболонка буде виходити з ладу exec.

Може хтось допоможе мені зрозуміти, чому це відбувається?


1
Я поняття не маю, чому це так. Ви маєте рацію; пошуку файлу робить його виконання в інтерактивному режимі . Можливо, ви знайшли помилку bash?
Wildcard

Відповіді:


2

Як я відчуваю, це помилка. Нижче наводиться якась "детективна історія".

Так, у exec.defкоді ми бачимо:

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

таким чином, execможе викликати вихід оболонки а) на неінтерактивну оболонку з false"no_exit_on_failed_exec" b) під execчас запуску в нижній частині

Для інтерактивної оболонки, якщо команда існує, оболонка замінюється командою, що викликається:

виконувати

Ця вбудована оболонка замінює поточний процес заданою командою. Зазвичай, коли оболонка стикається з командою, вона змушує дочірній процес фактично виконувати команду. Використовуючи вбудований exec, оболонка не розщеплюється, а команда exec'ed замінює оболонку . Тому при використанні в скрипті він примушує вихід із сценарію, коли команда exec'ed закінчується .

Приклад 15-24. Ефекти виконання

#!/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команда не викликає subshell - вона змушує всі команди виконуватись у вашій поточній, активній оболонці (вона використовується, наприклад, для встановлення змінних - не для деякої підпакеті, яка вийде, а для вашої поточної оболонки). Тож після виконання команди як з sourceоболонкою, так і безпосередньо в оболонці, ваша оболонка припиниться (це очікувана поведінка).

Коли сценарій запускається з використанням "source", він запускається в межах наявної оболонки, будь-які змінні, створені або модифіковані сценарієм, залишаться доступними після завершення сценарію. 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) під час пошуку вбудованої 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виконує команди в поточній оболонці, але встановлює прапор FEVAL_NONINT 0x008- неінтерактивний (помилка тут):, evalfile.cу source_file:

338 flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

Я створив проблему в трекері помилок:

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

Подивився б це.


EDIT1: Як прокоментував білет прихильник басу

"Оболонка наразі не є інтерактивною, коли читає аргумент файлу до вбудованого джерела (interactive == 0), хоча сама оболонка є інтерактивною (interactive_shell == 1)."

Також, за його словами, ця поведінка, швидше за все, не зміниться найближчим часом.

Питання зараз здається закритим.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.