What does [ -t 1 ] check?
[]
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.
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 onhost.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.
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).