Bash – Commands leaking characters to bash

bashexpectshellstdinstdout

While building a small expect script I noticed that, after executing the script, some characters were automatically on the input of bash. I have seen that on other programs before, but here I have something reproducable.

My expect script is the following:

#!/usr/bin/expect
spawn ssh root@[lindex $argv 0]
expect "password:"
send "[lindex $argv 1]\r"
expect "~#"

And this is the result:

user@PC:~$ expect test.exp 192.168.0.2 root
spawn ssh root@192.168.0.2
root@192.168.0.2's password:

^[[51;117Rroot@device:~# user@PC:~$ ;117R
-bash: syntax error near unexpected token `;'
user@PC:~$

So as you can see on the fifth line of the output, there are some random characters on stdout (^[[51;117R) and some of them leak onto the shell input line (;117R). If I press enter after executing the expect script, bash then tries to interpret that (;117R) as if I had entered it into bash.

My question is, what happens here and how do I stop it?

Best Answer

These are not "random characters".

% printf '\e[51;117R' | console-decode-ecma48
CPR 51;117
%

Something run during the log-on process for the remote system is, by sending a Device Status Report control sequence to its terminal, requesting a Cursor Position Report from that terminal. The terminal (your terminal, several hops away, passed the DSR output via ssh and expect) is duly generating and sending it.

% printf '\x1b[6n' ; console-decode-ecma48
^[[30;1R
CPR 30;1
LF
%

Such reports arrive from a terminal to the host (i.e. the machine directly connected to the terminal) in exactly the same way as typed input does. Programs running on the host have no reliable way to know that you did not simply type that Cursor Position Report. Of course expect is not reading and sending on terminal input at that point, so the CPR has queued up to be read by the next program that does read input from your terminal, which is your shell after expect has finished. What you are seeing is how your shell reacts to such control sequences as input.

Such things are one of the reasons that terminal input processing should really always be a proper ECMA-48 decoder with a proper state machine. Your shell's line editing system is not. (None of ZLE, Readline, or libedit really handle terminal input control sequences properly.) It is decoding things using pattern matching, which does not do the right thing. Notice how it has only ignored the first four characters of the control sequence. A proper ECMA-48 input decoder would decode the whole control sequence, including all of the Parameter characters up to the Final character, recognize it as a CPR, and (one hopes) throw it away as input for which it has no use.

As to what is requesting the CPR in the first place: For that you'll have to inspect the remote system, and what programs it runs at terminal log-on. A likely culprit is the Xterm resize program. (Note that you should not use that program when Xterm is not your terminal, as it will incorrectly set the TERM environment variable to an xterm type.) But resize is not the sole possibility.

Further reading

Related Question