Last year, I wrote about Filezilla: figuring out the cause of “Connection timed out after 20 seconds of inactivity” about sftp
connection problems.
The solution there was to exclude part of bashrc
with an if
[Wayback] statement so bash
would skip it during sftp
, but not during ssh
login:
[WayBack] linux – Use .bashrc without breaking sftp – Server Fault
- From answer 1 (thanks [WayBack] Mike):
Try doing this instead
if [ "$SSH_TTY" ]
then
source .bashc_real
fi
- From Answer 2 (thanks [WayBack] Insyte):
A good trick for testing the cleanliness of your login environment is to ssh
in with a command, which simulates the same way scp
/sftp
connect. For example: ssh myhost /bin/true
will show you exactly what scp
/sftp
sees when they connect.
That caused some scripts not to be run when switching user, for instance by doing sudo su -
.
The reason for that was that I forgot to put enough research in part of Answer 2, so I quote a few bits more of it (highlights and code markup mine):
… it’s worth pointing out that you can accomplish this carefully selecting which startup files to put the verbose stuff in. From the bash
man page:
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 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
.
The sftp
/scp
tools start an interactive non-login shell, so .bashrc
will be sourced.
For further reading, there is the underlying bash
manual as a PDF file [Wayback] and html
document tree [Wayback]. Note it is large (the PDF is 190 pages).
I find the easiest way to navigate around bash documentation through these links:
Types of shell invocations: to login or to non-login and to interactive or to non-interactive
Basically, from the above answer there are [Archive.is] 4 types of shells (confirmed by these parts of the bash
documentation: [Wayback] Section 6.1: Invoking-Bash and [Wayback] Section 6.2: Bash-Startup-Files):
- interactive login
- interactive non-login
- non-interactive login
- non-interactive non-login
And there are various means the shells can start (ssh
, local console, …). The "$SSH_TTY"
trick only checks interactive login via ssh
, but fails to detect others.
So I did some digging for the correct information to log, which including the above are:
From theory to practice
After reading the above documentation links, I put the below code in the global .bashrc
(which of course caused trouble with sftp
, so I commented it out later):
echo "Option flags: '$-'"
echo "PS1: '$PS1'"
echo "shopt login_shell: '$(shopt login_shell)'"
echo "Parameter zero: '$0'"
[ "$SSH_TTY" ] ; echo "[ \"\$SSH_TTY\" ] outcome: $?"
And the output after these commands:
-
ssh user@host
Option flags: 'himBH'
PS1: '\u@\h:\w> '
shopt login_shell: 'login_shell on'
Parameter zero: '-bash'
[ "$SSH_TTY" ] outcome: 0
Verdict: interactive
, login
-
ssh user@host
followed by
sudo su -
Option flags: 'himBH'
PS1: '\[\]\h:\w #\[\] '
shopt login_shell: 'login_shell on'
Parameter zero: '-bash'
[ "$SSH_TTY" ] outcome: 1
Verdict: interactive
, login
-
ssh user@host
followed by
bash
Option flags: 'himBH'
PS1: '\u@\h:\w> '
shopt login_shell: 'login_shell off'
Parameter zero: 'bash'
[ "$SSH_TTY" ] outcome: 0
Verdict: interactive
, non-login
-
ssh user@host
followed by
sudo su -
then by
bash
Option flags: 'himBH'
PS1: '\[\]\h:\w #\[\] '
shopt login_shell: 'login_shell off'
Parameter zero: 'bash'
[ "$SSH_TTY" ] outcome: 1
Verdict: interactive
, non-login
-
ssh user@host /bin/true
Option flags: 'hBc'
PS1: ''
shopt login_shell: 'login_shell off'
Parameter zero: 'bash'
[ "$SSH_TTY" ] outcome: 1
Verdict: non-interactive
, non-login
The final one is what for instance sftp
will see. It excludes the non-interactive
mark in the shopt
option flags.
Modifications to my .bashrc
file
Since the [Wayback] test
for "$SSH_TTY"
is inconsistent with the login being interactive, I modified the .bashrc
section
if [ "$SSH_TTY" ]
then
source .bashc_real
fi
to become
if [[ $- =~ i ]]
then
# only during interactive login shells
source .bashc_real
fi
I know the [[...]]
over test
shorthand [...]
is a bashism, see [Wayback] if statement – Is double square brackets [[ ]] preferable over single square brackets [ ] in Bash? – Stack Overflow for why I like it.
More relevant documentation
I based the above changes not only on the mentioned StackOverflow post, but also doing some more Googling revealing these useful documentation and question/answer links:
- [Wayback] Section 3.2.5.2: Conditional Constructs;
[[ expression ]]
[[…]]
Return a status of 0
or 1
depending on the evaluation of the conditional expression expression. Expressions are composed of the primaries described below in Bash Conditional Expressions. Word splitting and filename expansion are not performed on the words between the [[
and ]]
; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution, and quote removal are performed. Conditional operators such as ‘-f
’ must be unquoted to be recognized as primaries.
…
An additional binary operator, ‘=~
’, is available, with the same precedence as ‘==
’ and ‘!=
’. When it is used, the string to the right of the operator is considered a POSIX extended regular expression and matched accordingly (using the POSIX regcomp
and regexec
interfaces usually described in regex(3)
). The return value is 0
if the string matches the pattern, and 1
otherwise. If the regular expression is syntactically incorrect, the conditional expression’s return value is 2
.
- [Wayback] Section 4.1: Bourne Shell Builtins;
test
or [...]
(Bash Reference Manual)
Evaluate a conditional expression expr and return a status of 0
(true
) or 1
(false
). Each operator and operand must be a separate argument. Expressions are composed of the primaries described below in Bash Conditional Expressions. test
does not accept any options, nor does it accept and ignore an argument of --
as signifying the end of options.
When the [
form is used, the last argument to the command must be a ]
.
- [Wayback] bash – What are the special dollar sign shell variables? – Stack Overflow (thanks [Wayback] kojiro!):
$1
, $2
, $3
, … are the positional parameters.
"$@"
is an array-like construct of all positional parameters, {$1, $2, $3 ...}
.
"$*"
is the IFS expansion of all positional parameters, $1 $2 $3 ...
.
$#
is the number of positional parameters.
$-
current options set for the shell.
$$
pid of the current shell (not subshell).
$_
most recent parameter (or the abs path of the command to start the current shell immediately after startup).
$IFS
is the (input) field separator.
$?
is the most recent foreground pipeline exit status.
$!
is the PID of the most recent background command.
$0
is the name of the shell or shell script.
Most of the above can be found under Special Parameters in the Bash Reference Manual. There are all the environment variables set by the shell.
For a comprehensive index, please see the Reference Manual Variable Index.
- [Wayback] bash – Differentiate Interactive login and non-interactive non-login shell – Ask Ubuntu (thanks [Wayback] terdon)
Briefly (see here for more details), with examples:
- interactive login shell: You log into a remote computer via, for example
ssh
. Alternatively, you drop to a tty on your local machine (Ctrl+Alt+F1) and log in there.
- interactive non-login shell: Open a new terminal.
- non-interactive non-login shell: Run a script. All scripts run in their own subshell and this shell is not interactive. It only opens to execute the script and closes immediately once the script is finished.
- non-interactive login shell: This is extremely rare, and you’re unlikey to encounter it. One way of launching one is
echo command | ssh server
. When ssh
is launched without a command (so ssh
instead of ssh command
which will run command
on the remote shell) it starts a login shell. If the stdin
of the ssh
is not a tty, it starts a non-interactive shell. This is why echo command | ssh server
will launch a non-interactive login shell. You can also start one with bash -l -c command
.
If you want to play around with this, you can test for the various types of shell as follows:
Putting all this together, here’s one of each possible type of shell:
$ echo $-; shopt login_shell
himBHs
login_shell off
$ bash -l
$ echo $-; shopt login_shell
himBHs
login_shell on
$ bash -c 'echo $-; shopt login_shell'
hBc
login_shell off
$ echo 'echo $-; shopt login_shell' | ssh localhost
Pseudo-terminal will not be allocated because stdin is not a terminal.
hBs
login_shell on
- [Wayback] Difference between Login Shell and Non-Login Shell? – Unix & Linux Stack Exchange
A login shell is the first process that executes under your user ID when you log in for an interactive session. The login process tells the shell to behave as a login shell with a convention: passing argument 0, which is normally the name of the shell executable, with a -
character prepended (e.g. -bash
whereas it would normally be bash
. Login shells typically read a file that does things like setting environment variables: /etc/profile
and ~/.profile
for the traditional Bourne shell, ~/.bash_profile
additionally for bash†, /etc/zprofile
and ~/.zprofile
for zsh†, /etc/csh.login
and ~/.login
for csh, etc.
When you log in on a text console, or through SSH, or with su -
, you get an interactive login shell. When you log in in graphical mode (on an X display manager), you don’t get a login shell, instead you get a session manager or a window manager.
It’s rare to run a non-interactive login shell, but some X settings do that when you log in with a display manager, so as to arrange to read the profile files. Other settings (this depends on the distribution and on the display manager) read /etc/profile
and ~/.profile
explicitly, or don’t read them. Another way to get a non-interactive login shell is to log in remotely with a command passed through standard input which is not a terminal, e.g. ssh example.com <my-script-which-is-stored-locally
(as opposed to ssh example.com my-script-which-is-on-the-remote-machine
, which runs a non-interactive, non-login shell).
When you start a shell in a terminal in an existing session (screen, X terminal, Emacs terminal buffer, a shell inside another, etc.), you get an interactive, non-login shell. That shell might read a shell configuration file (~/.bashrc
for bash invoked as bash
, /etc/zshrc
and ~/.zshrc
for zsh, /etc/csh.cshrc
and ~/.cshrc
for csh, the file indicated by the ENV
variable for POSIX/XSI-compliant shells such as dash, ksh, and bash when invoked as sh
, $ENV
if set and ~/.mkshrc
for mksh, etc.).
When a shell runs a script or a command passed on its command line, it’s a non-interactive, non-login shell. Such shells run all the time: it’s very common that when a program calls another program, it really runs a tiny script in a shell to invoke that other program. Some shells read a startup file in this case (bash runs the file indicated by the BASH_ENV
variable, zsh runs /etc/zshenv
and ~/.zshenv
), but this is risky: the shell can be invoked in all sorts of contexts, and there’s hardly anything you can do that might not break something.
† I’m simplifying a little, see the manual for the gory details.
If you want to avoid the [[...]]
bashishm, then read [Wayback] Bashism: How to make bash scripts work in dash – Greg’s Wiki.
–jeroen
Like this:
Like Loading...
Related
Leave a Reply