pythonssh

Make ssh run a local script when invoked by subprocess.Popen


I have this code that executes shell commands on remote hosts:

cmd_list = shlex.split(cmd)
proc = subprocess.Popen(cmd_list, stdin=sys.stdin, stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE)

I am trying to run this command (based on this answer):

'ssh', '<hostname>', 'sudo', 'tar', 'xzf', '<path_to_tar_file>', '-C', '<some_directory>', '&&', 'bash', '-s', '<', '/Users/myself/.../myscript.sh'

However, it fails with this error:

bash: /Users/myself/.../myscript.sh: No such file or directory

How do I make it read from my local file and feed it to bash -s on the remote host?


Solution

  • Remember that both && and < are interpreted by the shell, and when you run a program with Popen like that, there is no shell involved (unless you set shell=True, but that way lies madness). This means that both the && and the < are sent to, and interpreted by, the shell that gets started by ssh on the remote machine, which is probably what you want for the former, but not for the latter.

    You want to open the file locally, and feed it as stdin to the process started by Popen:

    import subprocess
    
    cmd = ['ssh', 'host.example.com', 'echo', 'hello world', '&&', 'bash', '-s']
    
    with (open('commands.sh') as cmdfile,
          subprocess.Popen(cmd, stdin=cmdfile) as proc):
        proc.communicate()