74

How can I tell (in ~/.bashrc) if I'm running in interactive mode, or, say, executing a command over ssh. I want to avoid printing of ANSI escape sequences in .bashrc if it's the latter.

1
  • 1
    Choosing whether to print escape sequences or not is better to be based on $TERM value and not on interactiveness of the shell. The variable identifies capabilities of the client terminal which is the part which interprets the escape sequences.
    – yrk
    Sep 26, 2018 at 16:21

6 Answers 6

82

According to man bash:

PS1 is set and $- includes i if bash is interactive, allowing a shell script or a startup file to test this state.

So you can use:

if [[ $- == *i* ]]
then
    do_interactive_stuff
fi

Also:

When an interactive shell that is not a login shell is started, bash reads and executes commands from /etc/bash.bashrc and ~/.bashrc, if these files exist.

So ~/.bashrc is only sourced for interactive shells. Sometimes, people source it from ~/.bash_profile or ~/.profile which is incorrect since it interferes with the expected behavior. If you want to simplify maintenance of code that is common, you should use a separate file to contain the common code and source it independently from both rc files.

It's best if there's no output to stdout from login rc files such as ~/.bash_profile or ~/.profile since it can interfere with the proper operation of rsync for example.

In any case, it's still a good idea to test for interactivity since incorrect configuration may exist.

14
  • 11
    Note that $- may contain an i, not necessarily equal it. I use [[ $- =~ i ]] && echo interactive Feb 27, 2013 at 14:47
  • 6
    @AlexHowansky: the asterisks in the equality test make it a test for containing i Feb 27, 2013 at 18:17
  • 2
    Oh wow didn't even notice those, they look like double quotes on my monitor. It may be time to up the font size. <getting old> Feb 28, 2013 at 14:29
  • 3
    @PiotrDobrogost: This is an excellent discussion of shell startup files. Mar 6, 2014 at 2:11
  • 1
    @justin.m.chase: This question and answer are specific to Bash. I'm not sure how that applies to a C application. In any case, you should post a new question with sufficient detail in order to get an answer. Jun 23, 2022 at 23:34
32

the test tool can check for this (from the man page):

 -t FD          True if FD is opened on a terminal.

So you can use for example:

 if [ -t 0 ] ; then
    echo stdin is a terminal
   .....
 fi

or

if [ -t 1 ] ; then
    echo stdout is a terminal
 fi
4
  • Nice and seems to be portable between shells. TEST it bash <<< 'test -t 0 && echo Y || echo X' writes Y, bash -c 'test -t 0 && echo Y || echo X' writes X
    – kyb
    May 21, 2018 at 12:53
  • 2
    this also verifies that the standard input is a TTY; although it can be related but it is NOT the same as the shell's interactive mode, which is requested and indicated by shell's "-i" flag.
    – yrk
    Sep 26, 2018 at 16:18
  • 1
    This seems to work for script called by another script where $PS1 or $- may not work May 16, 2021 at 7:23
  • @ThanhTrung That would be the expected behaviour I think -- if the script is called, it's not running interactively, but if it's sourced, I think it would be Jul 5, 2022 at 16:10
14

Use:

if tty -s; then echo interactive; fi
2
  • this doesn't seem to work for me either, I think this is the correct answer: stackoverflow.com/a/49064632/1223975 Mar 2, 2018 at 7:58
  • 1
    this checks for presence of a TTY on standard input; although it can be related but it is not the same as shell's interactive mode, which is indicated by shell's "-i" flag.
    – yrk
    Sep 26, 2018 at 16:15
6

I typically look at the output of the program tty.

If you're on a tty, it will tell you which tty you're on. If you're not in interactive mode, it will typically tell you something like "not a tty".

2
  • 4
    tty -s will set a return value of 0 if you are on a terminal or 1 otherwise without giving you output. You can use it as 'if tty -s; then _interactive; fi'
    – BillThor
    May 31, 2010 at 1:31
  • 1
    Thanks! It's been a long time since I've needed to do this sort of thing and I guess I forgot some of the details...
    – chris
    May 31, 2010 at 11:13
4

This is how red hat does it... Guessing it's the fastest way...

if [ "${-#*i}" == "$-" ]; then

It means get the bash parameters, do a substring removal, shortest possible, match everything between the beginning and i. Then check if it's the same as the original bash parameters.

Check you did your job by connecting to the user using sftp, it will fail if non interactive sessions have output

1

This is half-an-answer. It's more of an addon/reference to the other answers.

man bash definition

I find the definition hard to understand: https://manpages.debian.org/bookworm/bash/bash.1.en.html

An interactive shell is one started without non-option arguments (unless -s
is specified) 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.

ABS lists several methods

The ABS (Advanced Bash Scripting Guide) lists several methods. None of them are great. And they are not equivalent.

https://tldp.org/LDP/abs/html/intandnonint.html

FYI: My trouble on Rocky

I've had some trouble on "Rocky Linux release 9.2 (Blue Onyx)" with a service startup script that insisted it was running interactively.

There was a trap 17 (signal SIGCHLD) in there. This traps whenever "Job Control" is enabled. And by default: Job Control is disabled inside non-interactive scripts and enabled inside interactive scripts.

Now for some reason Job Control enabled itself on this taste of Linux. And therefor the SIGCHLD trapped and the startup script failed. This worked fine on a different distro. I'm still in the process of analyzing this. So that's all I know for now.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .