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

9,505

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.

Share:
9,505

Related videos on Youtube

Wildcard
Author by

Wildcard

Updated on September 18, 2022

Comments

  • Wildcard
    Wildcard over 1 year

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

    • Admin
      Admin over 7 years
    • Admin
      Admin about 2 years
      Perhaps we can just declare that "has i in the value of $-" is the definition of shell interactiveness, and thus it is the source of truth. Then the first answer to "where is it stored" would be in $- and the answer to "can a shell become interactive or vice versa" is yes, clearly. Now that simplifies your questions but then makes the definition of "interactive" more complex. But beyond the implications in the shell itself, maybe its definition is simply intent. Processes invoked by a (non)interactive shell should attempt to respect that intent though it may not be technically enforced.
    • Admin
      Admin about 2 years
      Also, if you have a C program (for example) and its executed from an interactive prompt, how can that program get the value of $-? How are we supposed to know that intent from a non-shell program? Is that stored in a flag somewhere on the process?
  • Wildcard
    Wildcard over 7 years
    The two particular examples of behavior that I cite—shopt -u interactive_comments, and the "exit or not-exit" after-effect of ${parameter:?word} with unset parameter—do not have documented means of changing their behavior other than the question, "Is the shell interactive?" So even if it's impossible (or purposeless) to change those behaviors, the logical fact remains that there must be some place where the shell is remembering those options, independent of [ -t 0 ], [ -t 2 ] or [[ $- == *i* ]]. I want to know what that place is.
  • Stéphane Chazelas
    Stéphane Chazelas over 7 years
    @Wildcard, if you do exec < <(sleep infinity) in an interactive shell, you've got an interactive shell that is not very interactive. The shell would still consider itself interactive in that $- would still contain i, but you might not. I'm not sure there's much more to discuss about it.
  • Stéphane Chazelas
    Stéphane Chazelas over 7 years
    @Wildcard, if the shell was started as interactive, it remains so. The fact that you could do set +i in older bash versions was a bug.
  • Wildcard
    Wildcard over 7 years
    That's good to know; set +i is bizarre. Makes sense it's a bug. But how is it that (using Bash 4.2) my "testfile" script could differentiate between the two times it was run at all, and produce the output that it does, when $- didn't contain i anymore (or contained i incorrectly)? Is there some more fundamental storage place for this attribute than $- (that perhaps was mistakenly allowed to be out of sync with $- before Bash 4.4)?
  • Stéphane Chazelas
    Stéphane Chazelas over 7 years
    @Wildcard, If you look at the source code of bash, you'll probably find there's a interactive global boolean variable that maps to the i flag of $-. Changing that boolean will not automatically change all the behaviours of interactive shells. The changing from interactive to non-interactive is not supported.
  • Wildcard
    Wildcard over 7 years
    That's exactly the sort of definiteness I've been trying for. Thank you! Would you mind editing that into your answer?
  • Gilles 'SO- stop being evil'
    Gilles 'SO- stop being evil' over 7 years
    @Wildcard Dash accepts set +i and stops displaying a prompt. This is something that the user isn't supposed to do, so it isn't surprising that the behavior depends on the shell and is often accidental rather than the result of a deliberate decision by the shell implementer.