helpThe GNU Bourne-Again SHell - Support: sr #108980, Unexpected behaviour - sourcing...

 
 

sr #108980: Unexpected behaviour - sourcing file that contains "exec something-non-existent" closes the shell

Submitted by:  None
Submitted on:  Tue 23 Feb 2016 11:36:36 AM UTC  
 
Category: NonePriority: 5 - Normal
Severity: 3 - NormalStatus: Postponed
Privacy: PublicAssigned to: None
Originator Email: -unavailable-Open/Closed: Open
Operating System: GNU/Linux

Add a New Comment(Rich Markup)
   

You are not logged in

Please log in, so followups can be emailed to you.

 

Wed 24 Feb 2016 05:22:54 PM UTC, comment #1:

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). There
are other circumstances when an interactive shell is not
currently interactive (command substitution, executing traps,
running startup files, etc.)

I have not completely decided what to do about this, but I am not inclined right now to change the current behavior.

Chet Ramey <chet>
Project Administrator
Tue 23 Feb 2016 11:36:36 AM UTC, original submission:

Good local!

Some time ago the poster of this question on StackOverflow had found a strange behaviour.

exec1.sh:

#/bin/bash
exec "non-existing-file"

non-existing-file command does not exist.
Now run:

source exec1.sh

It closes the shell, as if it were non-interactive shell or existing command.

(Why)
(in the `exec.def` code, we see:

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

so, exiting shell would be done
a) on non-interactive shell with `false` "no_exit_on_failed_exec"
b) when `exec` is being run in subshell

For the interactive shell, if the command exist, the shell is replaced by the command invoked).

Now about source:

> 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

Will check it:
`source inter.sh`

*inter.sh:*
# Check if the shell is interactive
# based on http://www.tldp.org/LDP/abs/html/intandnonint.html

#!/bin/bash
echo "First method:"
echo $-
echo "Second method:"
if [[ -z $PS1 ]] # no prompt?
then
# non-interactive
echo "non-interactive"
else
# interactive
echo "interactive"
fi

The output:

> First method:
> himBH
> Second method:
> interactive


So, one may convince, `source`, does not fork, and uses current interactive shell.

The situation seems alike when just running `exec non-existent-file`. *So, why does it terminate while using `source`*?

I've compiled `bash-4.2` with `gcc` `-g` flag.

This is `exec1.sh`:

#!/bin/bash
exec "non-existing-file"

I've run it in `gdb` debugger.

chmod +x exec1.sh
gdb bash
r
./exec1.sh
./exec1.sh: line 2: exec: non-existing-file: not found

source exec1.sh
bash: exec: non-existing-file: not found
[Inferior 1 (process 13773) exited with code 0177]

What is 0177? It is not in standard bash exit codes. Though, some web search help us suggest, it is exit code for exec on "File not found".

For trace see attachment.

These are the last commands that being executed before exit:

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

Printing the variables:

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

It turns out, the shell is non-interactive (`interactive=0`) while sourceing the built-in `exec`. This is the reason for such behaviour.
It conradicts the documented behaviour, so, one may say, you've discovered a bug.

Where does `interactive` is set? (using `gdb's "watch" feature`)

source exec1.sh

Hardware watchpoint 2: interactive

Old value = 1
New value = 0
_evalfile (filename=0xa017a8 "exec1.sh", flags=14) at evalfile.c:226
226 return_catch_flag++;

And it is here: (`evalfile.c`)

223 if (flags & FEVAL_NONINT)
224 interactive = 0;

As one could obtain studying the call stack and stepping through code in gdb, sourcing the file really is performed this way: the whole file contents are combined as one big string in `evalfile.c`, the it is parsed to find separate strings and execute them with `evalstring.c`.
`evalfile`

(gdb) p flags
$1 = 14
#define FEVAL_NONINT 0x008
-> 14=1110 & 8=1000 -> 1000 -> interactive = 0 (non-interactive)
The problem is the 4th bit from the right in flags.

#1 0x0000000000485dcf in source_file (filename=0x9fb8c8 "exec1.sh", sflags=0)
at evalfile.c:344
344 rval = _evalfile (filename, flags);

Here is the relevant code of source_file:

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);

sflags=0, interactive_shell=1 here, so
flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

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

-> flags 1110 =14.

Result: interactive shell converts
flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

At last:
Why doing `source inter.sh` gives us `i` flag (interactive)?

using `watch` in `gdb` again:
`source inter.sh`
Hardware watchpoint 2: interactive

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

> First method:
> himBH
> Second method:
> interactive


The bash variables `$PS1` and `$-` and `interactive=0` in internals seem to be not in line with each other.

So, as I understand, it is a bug: `source` executes commands in the current shell, but sets flag `FEVAL_NONINT 0x008` - non-interactive
(bug here):
flags = FEVAL_BUILTIN|FEVAL_UNWINDPROT|FEVAL_NONINT;

Anonymous

 

(Note: upload size limit is set to 16384 kB, after insertion of the required escape characters.)

Attach File(s):
   
   
Comment:
   

Attached Files
file #36438:  trace.txt added by None (2KiB - text/plain)

 

Depends on the following items: None found

Items that depend on this one: None found

 

Carbon-Copy List
  • -unavailable- added by chet (Posted a comment)
  • -unavailable- added by None (Submitted the item)
  •  

    Do you think this task is very important?
    If so, you can click here to add your encouragement to it.
    This task has 0 encouragements so far.

    Only logged-in users can vote.

     

    Please enter the title of George Orwell's famous dystopian book (it's a date):

     

     

    Follow 2 latest changes.

    Date Changed By Updated Field Previous Value => Replaced By
    Wed 24 Feb 2016 05:22:54 PM UTCchetStatusNone=>Postponed
    Tue 23 Feb 2016 11:36:36 AM UTCNoneAttached File-=>Added trace.txt, #36438

    Back to the top


    Powered by Savane 3.1-cleanup1