Advanced Bash-Scripting Guide: An in-depth exploration of the art of shell scripting
Prev |  | Next

# Chapter 36. Miscellany

| Nobody really knows what the Bourne shell's grammar is. Even examination of the source code is little help. --Tom Duff

# 36.1. Interactive and non-interactive shells and scripts

An interactive shell reads commands from user input on a `tty`. Among other things, such a shell reads startup files on activation, displays a prompt, and enables job control by default. The user can interact with the shell.

A shell running a script is always a non-interactive shell. All the same, the script can still access its `tty`. It is even possible to emulate an interactive shell in a script.

       1 #!/bin/bash
       2 MY_PROMPT='$ '
       3 while :
       4 do
       5   echo -n "$MY_PROMPT"
       6   read line
       7   eval "$line"
       8   done
       9
      10 exit 0
      11
      12 # This example script, and much of the above explanation supplied by
      13 # Stéphane Chazelas (thanks again).

Let us consider an interactive script to be one that requires input from the user, usually with read statements (see Example 15-3). "Real life" is actually a bit messier than that. For now, assume an interactive script is bound to a tty, a script that a user has invoked from the console or an xterm.

Init and startup scripts are necessarily non-interactive, since they must run without human intervention. Many administrative and system maintenance scripts are likewise non-interactive. Unvarying repetitive tasks cry out for automation by non-interactive scripts.

Non-interactive scripts can run in the background, but interactive ones hang, waiting for input that never comes. Handle that difficulty by having an expect script or embedded here document feed input to an interactive script running as a background job. In the simplest case, redirect a file to supply input to a read statement (read variable <file). These particular workarounds make possible general purpose scripts that run in either interactive or non-interactive modes.

If a script needs to test whether it is running in an interactive shell, it is simply a matter of finding whether the prompt variable, $PS1 is set. (If the user is being prompted for input, then the script needs to display a prompt.)

       1 if [ -z $PS1 ] # no prompt?
       2 ### if [ -v PS1 ]   # On Bash 4.2+ ...
       3 then
       4   # non-interactive
       5   ...
       6 else
       7   # interactive
       8   ...
       9 fi

Alternatively, the script can test for the presence of option "i" in the $- flag.

       1 case $- in
       2 *i*)    # interactive shell
       3 ;;
       4 *)      # non-interactive shell
       5 ;;
       6 # (Courtesy of "UNIX F.A.Q.," 1993)

However, John Lange describes an alternative method, using the -t test operator.

       1 # Test for a terminal!
       2
       3 fd=0   # stdin
       4
       5 #  As we recall, the -t test option checks whether the stdin, [ -t 0 ],
       6 #+ or stdout, [ -t 1 ], in a given script is running in a terminal.
       7 if [ -t "$fd" ]
       8 then
       9   echo interactive
      10 else
      11   echo non-interactive
      12 fi
      13
      14
      15 #  But, as John points out:
      16 #    if [ -t 0 ] works ... when you're logged in locally
      17 #    but fails when you invoke the command remotely via ssh.
      18 #    So for a true test you also have to test for a socket.
      19
      20 if [[ -t "$fd" || -p /dev/stdin ]]
      21 then
      22   echo interactive
      23 else
      24   echo non-interactive
      25 fi

| Scripts may be forced to run in interactive mode with the -i option or with a `#!/bin/bash -i` header. Be aware that this can cause erratic script behavior or show error messages even when no error is present.

Prev | Home | Next
Scripting With Style | Up | Shell Wrappers