Newlines from a background process aren't working while ssh is running

Viewed 190

I have a process that prints some lines of text running in the background. If I start ssh, the newline characters don't work properly.

user@localhost:~$ { sleep 5; echo -e "1\n2\n3" ; } &
[1] 26215
user@localhost:~$ 1
2
3
user@localhost:~$ { sleep 5; echo -e "1\n2\n3" ; } &
user@localhost:~$ ssh localhost # quickly
user@localhost:~$ 1
                   2
                    3

I would expect 2 and 3 to start at the beginning of a newline regardless of whether ssh is running or not. Can someone explain what's happening here? Is there a way to fix things so newlines still work?

Oddly, if I throw some \r into the mix the newlines seem to work again, although I need to replace the default echo newline with a \r too.

user@localhost:~$ { sleep 5; echo -en "1\r\n2\r\n3\r\n" ; } &
[1] 7066
user@localhost:~$ 1
2
3
1 Answers

By default in an interactive shell the tty/pty is in canonical mode and \n written to the tty will be automatically converted to \r\n.

When you ssh to a remote server to start an interactive shell, ssh will change the local tty to raw mode (or non-canonical mode) and in raw mode \n will not be converted to \r\n.

See the following example (highlighted some part with ^ chars):

$ tty
/dev/pts/2
$ stty -a -F /dev/pts/2
speed 38400 baud; rows 43; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = <undef>; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-iuclc ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
^^^^^               ^^^^^
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
     ^^^^^^
echoctl echoke -flusho -extproc
$
$ ssh -t 127.0.0.1 bash --norc
bash-5.1# tty
/dev/pts/13
bash-5.1# stty -a -F /dev/pts/2
speed 38400 baud; rows 43; columns 80; line = 0;
intr = ^C; quit = ^\; erase = ^H; kill = ^U; eof = ^D; eol = <undef>;
eol2 = <undef>; swtch = <undef>; start = ^Q; stop = <undef>; susp = ^Z;
rprnt = ^R; werase = ^W; lnext = ^V; discard = ^O; min = 1; time = 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
-iuclc -ixany -imaxbel iutf8
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
^^^^^^
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
      ^^^^^^^
echoctl echoke -flusho -extproc
bash-5.1#

As you've found out, to force the \r\n behavior you can explicitly use \r\n:

printf '1\r\n2\r\n3\r\n'

You can see stty's manual for more details about the misc flags.

Related