What does [ -t 1 ] check?

7,875

Solution 1

[] is shortcut of test command.

According to man test:

-t FD
True if FD is a file descriptor that is associated with a terminal.

So if you running bash as interactive shell (terminal - see this thread for terminology explanation), bash will be replaced by zsh.

More about .bash* files:

When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable. The --noprofile option may be used when the shell is started to inhibit this behavior.

When a login shell exits, bash reads and executes commands from the files ~/.bash_logout and /etc/bash.bash_logout, if the files exists.

When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists. This may be inhibited by using the --norc option. The --rcfile file option will force bash to read and execute commands from file instead of ~/.bashrc.

Stéphane Chazelas comment:
Note that a shell can be interactive without stdout being a terminal, and a shell can be non-interactive with a terminal on stdout (like anytime you run a script within a terminal without redirecting/piping its output), and bash can read .bashrc even when not interactive (like in ssh host cmd where bash is the login shell of the user on host, or bash --login -c 'some code'). case $- in *i*)... is the correct way to test if a shell is interactive.

Solution 2

The test command [ -t 1 ] checks whether bash's output is on a terminal. The intent of this line is clearly to run zsh when opening a terminal, without disrupting other uses of bash. But it's done very badly.

The file .bashrc is read in three circumstances:

  • When bash is executed as an interactive shell, i.e. to run commands typed by the user rather than to execute batch commands.
  • When bash is a non-interactive shell which is run by an RSH or SSH daemon (typically because you run ssh host.example.com somecommand and bash is your login shell on host.example.com).
  • When it's invoked explicitly, e.g. in a user's .bash_profile (bash's choice of startup files is a bit weird).

[ -t 1 ] is a poor way to detect interactive shells. It's possible, but rare, to run bash interactively with standard output not going to a terminal. It's more common to have standard output going to a terminal in a non-interactive shell; a non-interactive shell has no business running .bashrc but unfortunately bash shells invoked by SSH do. There's a much better way: bash (and any other sh-style shell) provides a built-in, reliable method to do it.

case $- in
  *i*) echo this shell is interactive;;
  *) echo this shell is not interactive;;
esac

So “launch zsh if this is an interactive shell” should be written

case $- in
  *i*) exec zsh;;
esac

But even that is not a good idea: it prevents opening a bash shell, which is useful even if you use zsh. Forget about this blog post and instead simply configure your shortcut that opens a terminal to run zsh instead of bash. Don't arrange things so that “whenever you open the Bash application on Windows, it will now start up with the Zsh shell”: when you want zsh, open the Zsh application.

Solution 3

man 1 test:

-t FD

file descriptor FD is opened on a terminal

Your example executes (replaces running process, in this case bash) with zsh on if stdout is open on a terminal (not a file/pipe/etc).

Share:
7,875

Related videos on Youtube

Jin Kwon
Author by

Jin Kwon

Updated on September 18, 2022

Comments

  • Jin Kwon
    Jin Kwon almost 2 years

    I just found a way to start zsh when I start the bash on Windows from

    https://www.howtogeek.com/258518/how-to-use-zsh-or-another-shell-in-windows-10/.

    It recommended to add following code at the last of .bashrc.

    # Launch Zsh
    if [ -t 1 ]; then
    exec zsh
    fi
    

    What does [ -t 1 ] mean?

    Is it just true?

    Then, can I just do this?

    exec zsh
    
  • Stéphane Chazelas
    Stéphane Chazelas almost 7 years
    Note that a shell can be interactive without stdout being a terminal, and a shell can be non-interactive with a terminal on stdout (like anytime you run a script within a terminal without redirecting/piping its output), and bash can read .bashrc even when not interactive (like in ssh host cmd where bash is the login shell of the user on host, or bash --login -c 'some code' where the .bash_profile sources the .bashrc). case $- in *i*)... is the correct way to test if a shell is interactive.
  • mrc02_kr
    mrc02_kr almost 7 years
    @StéphaneChazelas good point. I will contain your comment in answer
  • Stéphane Chazelas
    Stéphane Chazelas almost 7 years
    For rsh/ssh and for interactive shell, it's if it's not a login shell. For login shells (sshd would not start a non-interactive login shell, but you could do it with ssh host exec bash -l), .bash_profile is read instead. Also note that for rsh/ssh, you also need $SHLVL to be unset or 0.
  • user3826906
    user3826906 over 6 years
    There are a number of different definitions of "interactive". If the shell thinks it is interactive, then i will be set (and in modern shells that can be tested with if instead of having to use case). But there are many use cases where one only cares if stdout (or stdin, or stderr...) is attached to a terminal.
  • αғsнιη
    αғsнιη over 3 years
    I suggest to use [[ -x /usr/bin/zsh && $- = *i* ]] && exec /usr/bin/zsh instead as it will checks first if zsh is installed (actually if there is executable/usr/bin/zsh) then so switch to zsh otherwise we will fail to login to bash terminal if that's not installed.