[Tutor] run local script on a remote machine

Wolfgang Maier wolfgang.maier at biologie.uni-freiburg.de
Thu Oct 27 03:57:55 EDT 2016


On 26.10.2016 19:44, Alex Kleider wrote:
>
> I've got three files as follows:
>

keeping just the relevant lines

...
> 2:
> #!/bin/bash
> #
> # file: call.sh
>
> # Demonstrates running a local python script on another host
> # with command line arguments specified locally.
>
> ssh -p22 alex at 10.10.10.10 python3 -u - one two three <
> /home/alex/Py/BackUp/Sandbox/Scripted/experiment.py
>
> 3:
> #!/usr/bin/env python3
> #
> # file: call.py
>
> import os
> import shlex
> import subprocess
>
> script = "/home/alex/Py/BackUp/Sandbox/Scripted/experiment.py"
> if os.path.isfile(script):
>     print("File exists on local machine.")
> else:
>     print("No such file.")
>
> command = (
> "ssh -p22 alex at 10.10.10.10 python3 -u - one two three < {}"
>     .format(script))
>
> ret = subprocess.call(shlex.split(command))
>

...

>
> Running the shell script (2) executes a single shell command and leaves
> the junk.txt file at 10.10.10.10 as desired.
> Calling the same shell command using the subprocess module from with in
> a python script (3) does not work:
> alex at X301n3:~/Py/BackUp/Sandbox/Scripted$ ./call.py
> File exists on local machine.
> bash: /home/alex/Py/BackUp/Sandbox/Scripted/experiment.py: No such file
> or directory

The structure of the command you are trying to execute would require you 
to set the "shell" argument of subprocess.call to True. Specifically, 
the "<" redirection operator is shell functionality.

Quoting from 
https://docs.python.org/3/library/subprocess.html?highlight=subprocess#subprocess.Popen:

"""
The shell argument (which defaults to False) specifies whether to use 
the shell as the program to execute. If shell is True, it is recommended 
to pass args as a string rather than as a sequence.

On POSIX with shell=True, the shell defaults to /bin/sh. If args is a 
string, the string specifies the command to execute through the shell. 
This means that the string must be formatted exactly as it would be when 
typed at the shell prompt. This includes, for example, quoting or 
backslash escaping filenames with spaces in them. If args is a sequence, 
the first item specifies the command string, and any additional items 
will be treated as additional arguments to the shell itself. That is to 
say, Popen does the equivalent of:

Popen(['/bin/sh', '-c', args[0], args[1], ...])
"""

This is exactly the behaviour you are expecting from your code: the 
shell gets called and sees a command for which the stdin should be 
replaced with the contents of a local file; it does that and executes 
the ssh command.
With the default shell=False, OTOH, the first item in your shlex.split 
generated list, 'ssh', becomes the executable and gets called with the 
rest of the list as arguments. ssh, however, does not interpret the '<' 
sign like the shell, runs the remote shell command, the remote shell 
sees and interprets the '<', fails to find the file on the remote 
machine and errors out.
The simple solution should be to not split your command string, but pass 
it directly to subprocess.call (untested):

subprocess.call(command, shell=True)

as long as you promise to NEVER use that code in production with user 
input. The problem with it is that it may allow users to inject shell 
commands as they like exactly because whatever ends up in the command 
string gets interpreted by the shell.

Best,
Wolfgang


More information about the Tutor mailing list