23

My problem is that I need to set a few variables, and output a few lines every time I login to the ssh shell, and at the same time I have to be able to use sftp to tarnsfer files via Filezilla.

Now, as per the openssh FAQ at http://www.openssh.org/faq.html, if your startup scripts echo any kind of output, it messes up with sftp. So it either delays indefinitely, or errors out with a "Connection closed by server with exit code 128".

I have tried solutions like moving .bashrc to .bash_profile, or using the following code in .bashrc:

if [ "$TERM" != "dumb" ]
then
   source .bashc_real
fi

And:

if [ "$TERM" = "xterm" ]
then
   source .bashc_real
fi

However, nothing works. My shell terminal is bash, and I connect to sftp with filezilla.

1
  • This is also important when you're using an IDE like PyCharm, which (might) use SFTP for remote-host file editing. Took me a while to figure out why one remote-box worked, and another broke.
    – Cyclops
    Jul 28, 2020 at 13:39

6 Answers 6

28

Try doing this instead

if [ "$SSH_TTY" ]
then
   source .bashc_real
fi
1
  • This worked the best in the following scenarios: sftp, ssh and switch to another user, sudo su from that other user back to me.
    – Ray Foss
    Dec 3, 2020 at 17:27
18

Mike's answer will probably work. But 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. Many distributions source .bashrc from .bash_profile or vice versa, so it can get confusing. 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.

A simple demo:

insyte@mazer:~$ echo "echo Hello from .profile" > .profile
insyte@mazer:~$ echo "echo Hello from .bashrc" > .bashrc

sazerac:~ insyte$ ssh mazer /bin/true
Hello from .bashrc
sazerac:~ insyte$

insyte@mazer:~$ rm .bashrc

sazerac:~ insyte$ ssh mazer /bin/true
sazerac:~ insyte$

The first test will cause scp/sftp/rsync etc. to break. The second version will work just fine.

4
  • I don't agree that "The sftp/scp tools start an interactive non-login shell" as we cannot really interact with the shell. But I don't understand why .bashrc would be source'd for scp or ssh host command.
    – SF.express
    Mar 26, 2013 at 8:23
  • 2
    The term "interactive" isn't subjective. It's a term used by bash to describe one of its startup modes. It is a fact that sftp/scp start an "interactive non-login shell". Feel free to argue with the developers about whether not sourcing .bashrc is appropriate in this case; I'm just telling you what it does.
    – Insyte
    Mar 26, 2013 at 15:16
  • 3
    Bash invoked by scp or ssh host command is indeed non-interactive. I just found this in bash manual: "Bash attempts to determine when it is being run with its standard input connected to a network connection, as when executed by the remote shell daemon, usually rshd, or the secure shell daemon sshd. If bash determines it is being run in this fashion, it reads and executes commands from ~/.bashrc, if that file exists and is readable." Here is the interesting history.
    – SF.express
    Mar 27, 2013 at 6:40
  • 1
    Also see the section Remote non login non interactive shells in this wiki page.
    – SF.express
    Mar 27, 2013 at 6:47
4

If you're using csh:

if ($?prompt)
  ... interactive stuff ...

And if it's bash:

if [[ $- == *i* ]]; then
  ... interactive stuff ...
fi

or alternatively using bash regular expressions:

if [[ $- =~ i ]]; then
  ... interactive stuff ...
fi

These lines should precede lines where you ouput/echo something back.

3
  • 1
    Hi, could you explain the code, please. I know that $? is the return level of the previous command. I dont understand $?prompt and $-, though. Or is csh specific? Mar 7, 2013 at 14:52
  • 2
    @Droidzone: $?var in csh returns 1 if var is defined and 0 otherwise. $- in bash would has the i char in its value if the shell is interactive.
    – SF.express
    Mar 26, 2013 at 8:14
  • 1
    Should update answer to explain $- is a special variable of shell options. see stackoverflow.com/questions/5163144/…
    – maninvan
    Jan 13, 2016 at 17:46
1

Mike's solution worked for me as well. But since my default shell is TCSH, I had to slightly edit the fix as follows (in .tcshrc):

if ( $?SSH_TTY ) then
    exec /bin/bash
endif

Just thought I would share for everyone's benefit.

1

Here are the first lines of my (default) .bashrc file:

# ~/.bashrc: executed by bash(1) for non-login shells.
# see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
# for examples

# If not running interactively, don't do anything
case $- in
    *i*) ;;
      *) return;;
esac

The check for an interactive session avoids messing up with SCP, SFTP or ssh remote-host command mode.

Without this, if your file .bashrc uses echo or some other stuff printing on stdout, you may get this kind of errors:

  • SFTP : Received message too long 168435779
  • SCP : protocol error: unexpected <newline>
0

I like some of the other solutions mentioned here better, but I thought I throw out the solution that I currently use on my bash and csh VMs to prevent SFTP disconnects due to echo commands in my startup scripts, just in case anyone finds the information helpful.

In BASH:

if [ $TERM == "xterm" ] || [ $TERM == "xterm-256color" ]; then
  echo "Xterm display identified: echo enabled"
  echo_disable="0"
else
  echo_disable="1"
fi

# Use the following for all subsequent echo commands
if [ $echo_disable == 0 ]; then
 echo "Safe to display on Xterm"
fi

In csh:

if ($TERM == "xterm") then
  echo "Xterm display identified: echo enabled"
  set echo_disable = "0"
else
  set echo_disable = "1"
endif

# Use the following for all subsequent echo commands
if !( "$echo_disable" ) echo "Safe to display on Xterm"

It's a bit brute force, but it does work.

2
  • I tried using the [ "$SSH_TTY" ] code above and found that it only worked for simple client programs like putty. When I use NoMachine it doesn't provide any output. This is why I needed to include "xterm-256color" in the code above.
    – Bob Noonan
    Oct 29, 2019 at 15:29
  • Interestingly enough, using "if ( $?SSH_TTY ) then" also did not work for my csh VM. I checked and there is no SSH_TTY environment variable defined. So my code above may be helpful to others that have a similar situation.
    – Bob Noonan
    Oct 29, 2019 at 15:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .