scp \033H Message – Understanding and Fixing SSH Issues

scpssh

Having a weird scp issue:

$ scp user@server.org:~/test.txt ./
Password:
\033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H \033H

Password takes fine, but getting that weird \033H thing. And the file doesn't transfer. Anyone have any ideas?

Best Answer

The fix for this is typically simple. Usually you can inspect .bashrc, find the line or couple of lines near the top that are causing the problem, and move or delete them. The hard part is convincing people that this problem is actually real. Details follow, but if you just want to fix this problem, then you will only need to use this shorter first section.

The Problem, and How To Solve It

This happens when .bashrc in the user's home directory on the remote machine contains commands that produce output and that run even in noninteractive shells. Less commonly, it would also happen if the systemwide /etc/bash.bashrc contains such commands. The specific output varies depending on what is producing it. But the combination of receiving unexpected output and not having any transfers succeed or even start very strongly points toward that cause (especially when the server is a Debian or Ubuntu system). scp uses standard input and output to send and receive data, and if unrelated data are transmitted through them then it cannot transfer files.

If you are thinking, "That's impossible, .bashrc is only for interactive shells!" or otherwise are interested in a detailed explanation of why this occurs, see the second section, below.

This problem does not break normal SSHing. So assuming the system is configured to permit you to ssh in successfully for an interactive login shell, you can do that, open .bashrc in the remote user's home directory, and either remove or comment out (with #) the offending command or commands, if you don't need them. Or if you do need them, then move them below another command that aborts when the shell is noninteractive. Such a command may already be present. In Ubuntu and some other distros, users' .bashrc files and the systemwide /etc/bash.bashrc usually begin with such checks.

The default .bashrc file in Ubuntu, copied from /etc/skel when a user account is created, contains this code to check if the running shell is interactive and to prevent any further commands in the file from running if it is not:

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

Another common method, which some people use in their .bashrc files and which is currently used in the systemwide /etc/bash.bashrc file for all users, is:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

If the files have been replaced or modified from the default, then you may see either technique use in either file. There are other possible ways to check for interactive operation but they are uncommon. If the user has written their own .bashrc file from scratch or brought it over from another operating system that isn't Debian, Ubuntu, or another Debian derivative, then it is likely that they don't have such code at all. But if you need it, you can still add it.

Any command that produces output and appears in .bashrc or /etc/bash.bashrc, unless it appears after code like that shown above, will cause unexpected data to be sent at the start of an scp session, and will prevent scp from being able to transfer files.

If you recently edited one of these files yourself by adding a command to the top, then it should be easy for you to figure out the specific change that caused the problem. Even if not, the description above may well give you enough information.

However, I recommend that you edit your question with full details, including the contents of those files, whether or not you are able to solve the problem based on the explanation above. Remember that these are the files on the remote server, not the client machine. That should help others who find your question understand the problem, as well as making it possible for more specific advice to be given, if that is needed.

I believe the likelihood of your problem being caused by something entirely different to be very low, but either way, the added information should make it possible to know for sure. Other people than the author of this question who have similar problems and need help to solve them should of course not edit this question, but should post their own question.


Why The Problem Occurs

People often say that .bashrc is only for interactive shells, but that is a misconception, or at best a severe oversimplification. bash runs commands from ~/.bashrc and /etc/bash.basrhc when:

  1. the shell is interactive, or
  2. another startup script like /etc/profile or ~/.profile sources it, but also
  3. when bash is running neither interactively nor as a login shell but determines that it is probably being run as the initial shell in a remote connection.

What bash takes as sufficient evidence that it is a remote shell--and thus whether or not, in practice, this effect even occurs with SSH--depends mainly on how it was compiled, which varies across operating systems, and secondarily on the version of bash and the version of sshd that is being used.

On current Debian and Ubuntu systems, when bash runs as a non-interactive non-login shell, it checks if the SSH_CLIENT environment variable is set and nonempty. (It checks other things too, but for SSH on current Ubuntu systems, they don't reveal anything.) If so, and the SHLVL environment variable is set to a value less than 2--indicating that it is the session's initial shell--bash runs the commands in /etc/bash.bashrc and ~/.bashrc.

To verify this quickly without modifying configuration files, readers may pass those variables manually into bash -c '''s environment and trace what it reads, or examine the run_startup_files function in shell.c (which starts on line 1022 in that version).

A non-interactive non-login bash shell is what you get when you run a script with bash, either by executing it after setting the necessary permissions and giving it an appropriate hashbang line or by running bash your-script explicitly. It is also what you get when you make bash run a one-liner with the -c option, such as:

bash -c 'echo hello world'

As you would expect, the shell you get when you log in via SSH for an interactive session is an interactive login shell. That's what you get when you run a command like this (assuming it succeeds):

ssh user@server.org

But here's where the non-intuitive part comes in: the shell you get when you log in via SSH for a non-interactive session is a non-interactive non-login shell. That's what you get from running a single command via SSH:

ssh user@server.org command args...

That is, running a single command via SSH runs bash as the same kind of shell--a non-interactive, non-login shell--as when you run a single command locally (or within an already-established remote session) using bash -c.

Like ssh in general, scp causes a shell to be run on the remote machine as the remote user. This is still whatever they have configured as their shell, i.e., the shell listed in the user's entry in /etc/passwd or the output of getent passwd (which also gets set as the value of the $SHELL environment variable when they are logged in). Unless they have changed it by running chsh, this is the default user shell, which in Ubuntu is bash.

That's why commands like this run a non-interactive non-login bash shell on the remote server, too:

scp user@server.org:~/test.txt ./

Unless they are guarded by code to stop if the shell is non-interactive, commands in .bashrc or /etc/bash.bashrc are run by such a shell. If they produce output--intentionally or unintentionally--then scp will not be able to copy files.

Related Question