How to check if a shell is login/interactive/batch
I think I understand the differences between an interactive, a login and a batch shell. See the following links for more help:
- What is the difference between a ‘Login’ and an ‘Interactive’ bash shell (from the sister site: Server Fault)
- Difference between Login Shell and Non-Login Shell?
- 2.1: Types of shell: interactive and login shells (from A User’s Guide to the Z-Shell)
My question is, how can I test with a command/condition if I am on an interactive, a login or a batch shell?
I am looking for a command or condition (that returns true
or false
) and that I could also place in an if
statement. For example:
if [[ condition ]]
echo "This is a login shell"
fi
csh / tcsh
For csh
and tcsh
I have the following in my .cshrc
file:
if($?prompt) then # Only interactive shells set $prompt
...
endif
Specifically for tcsh
, the variable loginsh
is set for a login shell:
if($?loginsh) then # A login shell..
...
endif
(tcsh
also has a variable shlvl
which is set to the number of nested shells, where the login shell has a value of 1.)
Another way is to check the result of tty
if [ "`tty`" != "not a tty" ]; then
I’m assuming a bash
shell, or similar, since there is no shell listed in the tags.
To check if you are in an interactive shell:
[[ $- == *i* ]] && echo 'Interactive' || echo 'Not interactive'
To check if you are in a login shell:
shopt -q login_shell && echo 'Login shell' || echo 'Not login shell'
By "batch", I assume you mean "not interactive", so the check for an interactive shell should suffice.
In any Bourne-style shell, the i
option indicates whether the shell is interactive:
case $- in
*i*) echo "This shell is interactive";;
*) echo "This is a script";;
esac
There’s no portable and fully reliable way to test for a login shell. Ksh and zsh add l
to $-
. Bash sets the login_shell
option, which you can query with shopt -q login_shell
. Portably, test whether $0
starts with a -
: shells normally know that they’re login shells because the caller added a -
prefix to argument zero (normally the name or path of the executable). This fails to detect shell-specific ways of invoking a login shell (e.g. ash -l
).
i
is not the correct option to look for. -i
is to force an otherwise
non-interactive shell to become interactive. The correct auto-enabled
option is -s
, but Bash unfortunately does not handle this correctly.
You need to check whether $-
contains s
(this is granted to be
auto-activated) or whether it contains i
(this is not granted to
be auto-activated but officially only coupled to the -i
command line
option of the shell).
fish shell
Here’s the answer for fish
in case any other users stumble upon this page.
if status --is-interactive
# fish is interactive - that is, connected to a keyboard.
# do stuff...
end
if status --is-login
# fish is a login shell - that is, should perform login tasks such as setting up PATH
# do stuff...
end
echo "darn, I really wanted to have to use globs or at least a case statement"
fish docs ref
UNIX/Linux has a command to check if you are on a terminal.
if tty -s
then
echo Terminal
else
echo Not on a terminal
fi
You can check to see if stdin is a terminal:
if [ -t 0 ]
then
echo "Hit enter"
read ans
fi
To check whether a script runs in an interactive or non-interactive shell,
I check in my scripts for the presence of a prompt stored in the $PS1
variable:
if [ -z $PS1 ] # no prompt?
### if [ -v PS1 ] # On Bash 4.2+ ...
then
# non-interactive
...
else
# interactive
...
fi
This I learned here: https://www.tldp.org/LDP/abs/html/intandnonint.html
Curiously, scripts launched off the desktop in MacOS have the same environment as those started by hand. (They have their stdin as a teletype tty, they have their login_shell
shopt unset and their "$-"
as ehxB
)
If my preferred way of bringing a script to the desktop is to create a symlink from ~/Desktop
to the script, I can only guess that it was launched with a click by checking if "$0"
is an absolute path (i.e. starts with a slash).
for Zsh
# Checking Interactive v.s. Non-Interactive
[[ -o interactive ]] && echo "Interactive" || echo "Non-Interactive"
#
# Checking Login v.s. Non-Login
[[ -o login ]] && echo "Login" || echo "Non-Login"