Bash – Can an interactive shell become non-interactive or vice versa

bashshellshell-scriptterminology

Can an interactive shell become non-interactive or vice versa?

Note: I've done a lot of research on the basic question, "What is the difference between interactive and non-interactive?", and the results of my research led me to ask this question.

This question has a long preamble in part because it's crucial what type of definition we use for "interactive" in order to answer it. A definition could be an arbitrary label for a certain set; it could be descriptive of various properties; or it can give you information you can use to predict behavior and understand purpose. This last type we can call an "action definition" or a "dynamic definition" and it is the most useful.


In man 1p sh, the following definition of an interactive shell is given:

   If the -i option is present, or  if  there  are  no  operands  and  the
   shell’s  standard  input and standard error are attached to a terminal,
   the shell is considered to be interactive.

From the mention of "-i option" and use of the word "operands," this is referring to a shell's invocation, not attributes that could be examined in a running shell.

The Bash man page phrases it a little bit differently:

   An interactive shell is one started without  non-option  arguments  and
   without the -c option whose standard input and error are both connected
   to terminals (as determined by isatty(3)), or one started with  the  -i
   option.   PS1 is set and $- includes i if bash is interactive, allowing
   a shell script or a startup file to test this state.

The definition in the first sentence again only refers to the starting of a shell.

The second sentence (in my reading) defines conditions that are used as proxies to establish whether the shell was started in the particular ways that are defined as making it "interactive."

Note that I do not interpret this sentence as: "A bash shell is interactive if and only if $- includes 'i'." $- seems to just be a handy indicator, not a definition of interactive. This is crucially relevant to my question.


Both of these (the POSIX sh definition and Bash's) are mechanical definitions that tell in what circumstances the label "interactive" applies to a shell you've started. They aren't action definitions as they don't give any implications of this label.

However, I see that throughout the rest of the Bash man page are sprinkled references to the shell behaving in certain ways "unless it's an interactive shell" or "only in interactive shells, or if _____ option is set." (There are numerous examples and that's not the main point of this question.)

So I'll accept that "interactive" is just a convenient label for the collection of default "interactive" behaviors (option settings) described throughout the rest of the man page. It's not a fundamental term or object in itself; it has no authoritative definition outside of the shell's source code. (Unlike, for instance, the terms "open file descriptor" or "stopped process" which refer to abstractions built into the design of the kernel itself.)

(While it's also defined in the POSIX definition of sh, that man page [man 1p sh] has far fewer uses of "unless the shell is interactive" and similar statements than man bash has, and almost exclusively focuses on invocation-time differences, so I will focus on Bash from this point onward.)


Some of the implications of a shell being "interactive" are only relevant at invocation time anyway, such as which files are sourced by the shell before it reads other commands. However, there are implications (at least in Bash) that are relevant at any time. Thus there must be a way to tell, for any given running shell, whether it is interactive or not.

Running set +i in an interactive Bash shell causes 'i' to be removed from the contents of $-.

The question is: Does this actually mean the shell is no longer interactive?

By Bash's exact definition, it shouldn't, since nowhere in the definition is it required that 'i' be present in $-:

   An interactive shell is one started without  non-option  arguments  and
   without the -c option whose standard input and error are both connected
   to terminals (as determined by isatty(3)), or one started with  the  -i
   option.

A strict reading of the exact definition also raises the question: If the stdin or stderr of an interactive terminal are redirected so they are no longer connected to terminals, does the shell become non-interactive?

(It appears that the answer to this one is "no" and the man page could have included the modifier: "whose standard input and error are both connected to terminals…at the time of invocation," but I don't know definitively.)


If the answer is, "No, a shell cannot become non-interactive, nor vice versa," then what is the definitive way to determine if the shell is interactive?

Put yet another way: If there are behaviors of an "interactive shell" which persist after set +i, what is used to determine that those behaviors should continue to apply?


Lest anyone doubt it: There are behaviors of a shell invoked interactively which persist after set +i and behaviors of a shell invoked non-interactively which persist after set -i. For an example, consider the following excerpt from man bash:

COMMENTS
   In a non-interactive shell, or an interactive shell in which the inter-
   active_comments  option  to  the  shopt  builtin  is enabled (see SHELL
   BUILTIN COMMANDS below), a word beginning with # causes that  word  and
   all  remaining  characters  on that line to be ignored.  An interactive
   shell without the interactive_comments option enabled  does  not  allow
   comments.  The interactive_comments option is on by default in interac-
   tive shells.

Thus by unsetting the interactive_comments option we can see a difference between interactive and non-interactive shells. The persistence of this difference is demonstrated by the following script:

#!/bin/bash

# When the testfile is run interactively,
# all three comments will produce an error
# (even the third where 'i' is not in '$-').
# When run noninteractively, NO comment will
# produce an error, though the second comment
# is run while 'i' IS in '$-'.

cat >testfile <<'EOF'
shopt interactive_comments
shopt -u interactive_comments
shopt interactive_comments
echo $-
#first test comment
set -i
echo $-
#second test comment
set +i
echo $-
#third test comment
EOF

echo 'running bash -i <testfile'
bash -i <testfile
echo 'running bash <testfile'
bash <testfile

This confirms that "interactive" and "has i in the value of $-" are not equivalent.

A similar test using ${parameter:?word} with an unset parameter produces similar results, again confirming that $- is not the "source of truth" for shell interactiveness.


So, finally, where is the definitive 'interactiveness' trait of a shell stored?

And, can an interactive shell become non-interactive or vice versa? (…by changing this trait?)

Best Answer

The question I'd ask would be why would anyone want to do that?

You can disable some aspects of interactive shells like:

  • PS1= PS2= to disable the prompts
  • set +m to disable job control
  • disable history in some shells
  • you may be able to unload the zle and all completion modules in zsh.

But if you want the shell to stop being interactive, you could instead do:

. /some/file; exit

To tell it to get the rest of the commands from /some/file (replace with /dev/tty if you still want the commands to be read from the tty device), though there would still be some differences from non-interactive shells, like for the behaviour of return or the fact that it would still do job control or:

exec myshell /dev/tty

To replace your current interactive shell with a non-interactive one that still reads commands from the tty device.

Note that with bash 4.4, set +i returns with a bash: set: +i: invalid option like in most other shells.

Related Question