Ssh – Reading stdin from python program started via ssh in terminal

bashpythonssh

I have got this little python3 script test.py:

import sys

print('test1')
test1 = sys.stdin.read()
print('test2')
test2 = sys.stdin.read()

print(test1)
print(test2)

And I would like to run this script remotely via ssh like this:

ssh srvid 'cd; python3 test.py'

I would expect that program prints test1, then will wait for input, and then prints test2 and again wait for input.

But the behaviour is slightly different:

ssh srvid 'cd; python3 test.py'
hell
test1
test2
hell

The program first waited for input. I have entered hell and pressed enter and then ctr+d for eof. Script did not wait for second input, and printed out test1 and test2.

It seems, that stdin / stdout are somehow blocked.

I tried same example in bash:

#!/bin/bash

echo "Hello world";
read test;
echo "helloworld 2";
read test2;
echo $test;
echo $test2;

When I invoked this script via ssh, everything worked as I am expecting.

Can somebody help me?

Best Answer

This is due to a libc convention for stdio buffering. If stdout is a tty, it is typically line-buffered; fully buffered otherwise.

As others have suggested, passing the -t flag to ssh forces psuedo-tty allocation, and you get line-buffering as a result.

However, you could also explicitly flush stdout to get a similar result, as in:

import sys

print('test1')
sys.stdout.flush()
test1 = sys.stdin.readline() # ctrl+d and .read() effectively closes 
print('test2')               # stdin for the duration
sys.stdout.flush()
test2 = sys.stdin.readline()

print(test1)
print(test2)
# flushing happens implicitly when the stream is closed

Another option, would be to run python in totally unbuffered mode with the -u flag, as in

ssh srvid 'cd; python3 -u test.py'

Additional workarounds can be found in this stackoverflow question.


The program first waited for input. I have entered hell and pressed enter and then ctr+d for eof. Script did not wait for second input, and printed out test1 and test2.

I'm pretty sure the script didn't stop at the second input because you sent it an EOF during the first.


FWIW, I would probably write the python this way, unless you need multi-line input. It's a closer comparison to your bash example, IMO.

#!/usr/bin/env python3

print('test1')
test1 = input() # raw_input() in python 2.x
print('test2')         
test2 = input()

print(test1)
print(test2)