68

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.

Alex B
  • 1,654
  • 2
  • 16
  • 29
  • 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 '18 at 16:21

5 Answers5

77

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.

Dennis Williamson
  • 60,515
  • 14
  • 113
  • 148
  • 11
    Note that $- may *contain* an i, not necessarily equal it. I use `[[ $- =~ i ]] && echo interactive` – Alex Howansky Feb 27 '13 at 14:47
  • 4
    @AlexHowansky: the asterisks in the equality test make it a test for containing `i` – Dennis Williamson Feb 27 '13 at 18:17
  • 1
    Oh wow didn't even notice those, they look like double quotes on my monitor. It may be time to up the font size. – Alex Howansky Feb 28 '13 at 14:29
  • *Sometimes, people source it from `~/.bash_profile` or `~/.profile` which is incorrect since it interferes with the expected behavior* Right. What do you think of sourcing `~/.bashrc` from `~/.bash_login`? As login shell needn't to to be interactive I guess it's incorrect, too. – Piotr Dobrogost Mar 05 '14 at 20:44
  • 3
    @PiotrDobrogost: [This is an *excellent* discussion](http://superuser.com/a/183980/310) of shell startup files. – Dennis Williamson Mar 06 '14 at 02:11
  • So why not checking *also* PS1 if «PS1 is set and $- includes i if bash is interactive»? – Valerio Bozzolan May 20 '20 at 16:00
  • This didn't work for me on the mac. `bash -c 'echo $-'` == `hBc` – Gareth Davidson May 26 '21 at 21:05
  • @GarethDavidson that is the same on Ubuntu and what I'd expect (at least, what I require). Just type `bash` to run an interactive shell then `echo $-` returns `himBHs`. `bash -c 'echo $-'` is running the text command within the bash program non-interactively (there is no prompt, etc) – phil_ayres Sep 16 '21 at 17:34
  • How can I do this check from within a C application (or any other non shell script application)? – justin.m.chase Mar 12 '22 at 05:58
  • 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. – Dennis Williamson Jun 23 '22 at 23:34
  • @GarethDavidson: That's because your command is not running Bash in interactive mode (as phil_ayres indicates). The fact that `i` (for "interactive") is not included in your output is the primary indicator of that. It also shows that history (no `H`) is off, job control (no `m`) is disabled and commands are coming from the command line (`c`) instead of standard input (no `s`). All these are defaults for non-interactive shells. – Dennis Williamson Jun 25 '22 at 13:10
  • @DennisWilliamson I think the answer to my Q is that you cannot know this in a C application without an explicit flag. Typically applications will have their own `-i` flag but lacking that they may be able to look at the stdin pipe to determine if it is TTY. You can know to disable interactive mode if it is not tty but you can't know to enable it with TTY so you would typically require a specific cli flag to enable it. The state of being interactive in bash is a flag in bash itself and is not necessarily a env var or option or property that can be read by any process invoked by bash. – justin.m.chase Jul 13 '22 at 14:53
  • @justin.m.chase: As I mentioned earlier, you should ask a separate question. Or take a look at [this](https://rosettacode.org/wiki/Check_output_device_is_a_terminal) (in C, `isatty()`) for a start. Btw, a flag doesn't magically provide this functionality nor is one necessary in order to perform the detection, you will need to add code to your C application (whether ... – Dennis Williamson Jul 13 '22 at 16:25
  • ...or not you use flag code to control it) to do the detection. Your code can use conditionals to do different things depending on where `stdout` is going. If you do provide flag code, it can be used to override the default code. This is how Bash works. If you don't specify `-i`, it decides what to do depending on other conditions. – Dennis Williamson Jul 13 '22 at 16:25
27

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
user4514
  • 433
  • 4
  • 7
  • 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 '18 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 '18 at 16:18
  • This seems to work for script called by another script where $PS1 or $- may not work – Thanh Trung May 16 '21 at 07: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 – SpinUp __ A Davis Jul 05 '22 at 16:10
13

Use:

if tty -s; then echo interactive; fi
sorin
  • 7,668
  • 24
  • 75
  • 100
  • this doesn't seem to work for me either, I think this is the correct answer: https://stackoverflow.com/a/49064632/1223975 – Alexander Mills Mar 02 '18 at 07: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 '18 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".

chris
  • 11,784
  • 6
  • 41
  • 51
  • 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 '10 at 01: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 '10 at 11:13
3

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

Ray Foss
  • 239
  • 2
  • 12