Por que “exec-inexistente-file” sai do shell quando em um script que é originado?

6

A documentação para exec diz que ele sairá do shell em caso de erro se o shell atual não for interativo e a opção execfail não estiver configurada.

Eu notei um comportamento estranho em um shell bash interativo que não posso explicar com os documentos. Este comando

exec non-existent-file

produz uma mensagem de erro e o shell permanece ativo, conforme o esperado. Mas se eu colocar o mesmo comando em um arquivo e source desse arquivo, o shell atual será encerrado pelo executável com falha.

Alguém poderia me ajudar a entender por que isso acontece?

    
por Alexey Alexandrov 27.10.2015 / 01:14

1 resposta

2

Como eu sinto, é um bug. Abaixo está algum tipo de "história de detetive".

Sim, no código exec.def , vemos:

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

então, exec causaria a saída do shell a) no shell não interativo com false "no_exit_on_failed_exec" b) quando exec está sendo executado em subshell

Para o shell interativo, se o comando existir, o shell será substituído por o comando invocado:

exec

This shell builtin replaces the current process with a specified command. Normally, when the shell encounters a command, it forks off a child process to actually execute the command. Using the exec builtin, the shell does not fork, and the command exec'ed replaces the shell. When used in a script, therefore, it forces an exit from the script when the exec'ed command terminates.

Example 15-24. Effects of 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

O comando

source não chama subshell - força todos os comandos a executar no seu shell atual ativo (ele é usado, por exemplo, para definir variáveis - não para algum subshell que irá sair, mas para o seu shell atual). Então, depois de executar o comando com source e diretamente no shell, seu shell terminará (é o comportamento esperado).

When a script is run using 'source' it runs within the existing shell, any variables created or modified by the script will remain available after the script completes. http://ss64.com/bash/source.html

Eu compilei bash-4.2 e o executei em gdb debugger.

Estes são os últimos comandos que estão sendo executados antes da saída:

(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]

Imprimindo as variáveis:

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

O shell não é interativo ( interactive=0 ) durante a origem do exec embutido. Essa é a razão para tal comportamento. Isso contradiz o comportamento documentado, portanto, pode-se dizer que você encontrou um bug.

A alteração para non-interactive , interactive=0 , acontece aqui ( 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;

por causa de flags=14 . flags estão definidos em um nível mais alto, na função 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);

E as definições são:

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

- > sinalizadores 1110 = 14.

Então, pelo que entendi, é um erro: source executa comandos no shell atual, mas define o sinalizador FEVAL_NONINT 0x008 - não interativo (bug aqui): evalfile.c , em source_file :

338 flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

Eu criei um problema em um rastreador de bugs:

link

Assista.

EDIT1: Como o apoiador bash comentou no ticket

"The shell is not currently interactive when reading a file argument to the source builtin (interactive == 0), though the shell itself is interactive (interactive_shell == 1)."

Além disso, como ele disse, esse comportamento provavelmente não será alterado no futuro próximo.

A pergunta parece encerrada agora.

    
por 23.02.2016 / 12:46

Tags