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@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@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@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
_______________________________________________
Tutor maillist  -  Tutor@python.org
To unsubscribe or change subscription options:
https://mail.python.org/mailman/listinfo/tutor

Reply via email to