Search code examples
pythonsshpopenportforwarding

Why are these python print lines indented? Context: port forwarding with ssh using python Popen


I have this piece of code that is supposed to use subprocess.Popen to forward some ports in the background. It gives unexpectedly indented print statements -- any idea what went wrong here?

Also if you have a better way of port forwarding (in the background) with python, I'm all ears! I've just been opening another terminal and running the SSH command, but surely there's a way to do that programmatically, right?

The code:

import subprocess

print("connecting")
proc = subprocess.Popen(
    ["ssh", f"-L10005:[IP]:10121",
        f"[USERNAME]@[ANOTHER IP]"],
    stdout=subprocess.PIPE
    )
for _ in range(100):
    realtime_output = str(proc.stdout.readline(), "utf-8")
    if "[IP]" in realtime_output:
        print("connected")
        break

... other code that uses the forwarded ports

print("terminating")
proc.terminate()

Expected behavior (normal print lines):

$ python test.py
connecting
connected
terminating

Actual behavior (wacky print lines):

$ python test.py
connecting
connected
         terminating
                            $ [next prompt is here for some reason?]

Solution

  • This is likely because ssh is opening up a full shell on the remote machine (if you type in some commands, they'll probably be run remotely!). You should disable this by passing -N so it doesn't run anything. If you don't ever need to type anything into ssh (i.e. entering passwords or confirming host keys), you can also pass -n so it doesn't read from stdin at all. With that said, it looks like you also can do this entirely within Python with the Fabric library, specifically Connection.forward_local().

    The indented line weirdness is due to either ssh or the remote shell changing some terminal settings, one of which adds carriage returns before newlines that get sent to the terminal. When this is disabled, each line will start at the horizontal position of the end of the previous line:

    $ stty -onlcr; printf 'foo\nbar\nbaz\n'; stty onlcr
    foo
       bar
          baz
             $