I'm hacking some support for DomainKeys and DKIM into an open source email marketing program, which uses a python script to send the actual emails via SMTP. I decided to go the quick and dirty route, and just write a perl script that accepts an email message from STDIN, signs it, then returns it signed.
What I would like to do, is from the python script, pipe the email text that's in a string to the perl script, and store the result in another variable, so I can send the email signed. I'm not exactly a python guru, however, and I can't seem to find a good way to do this. I'm pretty sure I can use something like os.system for this, but piping a variable to the perl script is something that seems to elude me.
In short: How can I pipe a variable from a python script, to a perl script, and store the result in Python?
EDIT: I forgot to include that the system I'm working with only has python v2.3
Use subprocess. Here is the Python script:
#!/usr/bin/python
import subprocess
var = "world"
pipe = subprocess.Popen(["./x.pl", var], stdout=subprocess.PIPE)
result = pipe.stdout.read()
print result
And here is the Perl script:
#!/usr/bin/perl
use strict;
use warnings;
my $name = shift;
print "Hello $name!\n";
os.popen() will return a tuple with the stdin and stdout of the subprocess.
from subprocess import Popen, PIPE
p = Popen(['./foo.pl'], stdin=PIPE, stdout=PIPE)
p.stdin.write(the_input)
p.stdin.close()
the_output = p.stdout.read()
"I'm pretty sure I can use something like os.system for this, but piping a variable to the perl script is something that seems to elude me."
Correct. The subprocess module is like os.system, but provides the piping features you're looking for.
I'm sure there's a reason you're going down the route you've chosen, but why not just do the signing in Python?
How are you signing it? Maybe we could provide some assitance in writing a python implementation?
I tried also to do that only configure how to make it work as
pipe = subprocess.Popen(
['someperlfile.perl', 'param(s)'],
stdin=subprocess.PIPE
)
response = pipe.communicate()[0]
I wish this will assist u to make it work.
Related
Normally you can automate answers to an interactive prompt by piping stdin:
import subprocess as sp
cmd = 'rpmbuild --sign --buildroot {}/BUILDROOT -bb {}'.format(TMPDIR, specfile)
p = sp.Popen(cmd, stdout=sp.PIPE, stderr=sp.PIPE, stdin=sp.PIPE, universal_newline=True, shell=True)
for out in p.communicate(input='my gpg passphrase\n'):
print(out)
For whatever reason, this is not working for me. I've tried writing to p.stdin, before executing p.communicate(), I've tried flushing the buffer, I've tried using bytes without universal_newlines=True, I've hard coded things, etc. In all scenarios, the command is executed and hangs on:
Enter pass phrase:
My first hunch was that stdin was not the correct file descriptor and that rpmbuild was internally calling a gpg command, and maybe my input isn't piped. But when I do p.stdin.close() I get an OSerror about subprocess trying to write to the closed descriptor.
What is the rpmbuild command doing to stdin that prevents me from writing to it?
Is there a hack I can do? I tried echo "my passphrase" | rpmbuild .... as the command but that doesn't work.
I know I can do something with gpg like command and sign packages without a passphrase but I kind of want to avoid that.
EDIT:
After some more reading, I realize this is issue is common to commands that require password input, typically using a form of getpass.
I see a solution would be to use a library like pexpect, but I want something from the standard library. I am going to keep looking, but I think maybe i can try writing to something similar /dev/tty.
rpm uses getpass(3) which reopens /dev/tty.
There are 2 approaches to automating:
1) create a pseudotty
2) (linux) find the reopened file descriptor in /proc
If scripting, expect(1) has (or had) a short example with pseudotty's that can be used.
To elaborate on what I'm doing:
I want to create a web-based CLI for my Raspberry Pi. I want to take a websocket and connect it to this Raspberry Pi script, so that the text I type into the webpage will get entered directly into the CLI on the raspberry pi, and the response will return to me on the webpage.
My first goal is creating the python script that can properly send a user-inputted command to the CLI and return all responses in the CLI back.
If you just need the return value you can use os.system, but then you won't get the output of stdout and stderr. So you probably have to use the subprocess module, which requires you to split the input text into command and parameters first.
Sounds like you are looking for the python subprocess module in the standard library. This will allow you to interact with the CLI from a python script.
The subprocess module will do this for you but has a few quirks. You can pass in file objects to the various calls to bind to stderr and stdout, but they have to be real file objects. StringIO doesn't cut it.
The below uses check_output() as it grabs stdout for us and saves us opening a file. I'm sure there's fancier way of doing this.
from tempfile import TemporaryFile
from subprocess import check_output, CalledProcessError
def shell(command):
stdout = None
with TemporaryFile('rw') as fh:
try:
stdout = check_output(command, shell=True, stderr=fh)
except CalledProcessError:
pass
# Rewind the file handle to read from the beginning
fh.seek(0)
stderr = fh.read()
return stdout, stderr
print shell("echo hello")[0]
# hello
print shell("not_a_shell_command")[1]
# /bin/sh: 1: not_a_shell_command: not found
As one of the other posters mentions, you should really cleanse your input to prevent security exploits (and drop the shell=true). To be honest though, your project sounds like you are purposefully building a remote execution exploit for yourself, so it probably doesn't matter.
I'm trying to run a shell program through python. I need to run a command, then while it's still running and waiting for input to continue, I need to take the output received by the program, and process that data as a string. Then I need to parse some data into that program, and simulate an enter pressing.
What would be the best way to achieve this?
subprocess.Popen will work for this, but to read and then write and then read again you can't use communicate (because this will cause the process to end).
Instead, you'll need to work with the process's output pipe (process.stdout below). This is tricky to get right, because reading on the process's stdout is blocking, so you sort of need to know when to stop trying to read (or know how much output the process is going to produce).
In this example, the subprocess is a shell script that writes a line of output, and then echoes whatever you give it until it reads EOF.
import subprocess
COMMAND_LINE = 'echo "Hello World!" ; cat'
process = subprocess.Popen(COMMAND_LINE, shell=True,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
s = process.stdout.readline().strip()
print(s)
s2 = process.communicate(s)[0]
print(s2)
Gives:
Hello World!
Hello World!
For more complicated cases, you might think about looking at something like pexpect.
Use subprocess.Popen to run your shell application and use communicate to interact with it.
$whois abc.com
I want to use python to hit this command, and then give the result as a String of text. How can I do that?
You can use subprocess, for example:
from subprocess import Popen, PIPE
output = Popen(["/usr/bin/whois", "abc.com"], stdout = PIPE).communicate()[0]
The stdout = PIPE parameter forces stdout to be written to a temporary pipe instead of the console (if you don't want that, remove the stdout parameter).
subprocess is fine. On the other hand, the whois protocol is so simple that I do not see why to use an external command (and depend on its availability). Just open a TCP connection to port 43, send a one-line query and read the responses.
With subprocess.
I'm writing a python script that executes a csh script in Solaris 10. The csh script prompts the user for the root password (which I know) but I'm not sure how to make the python script answer the prompt with the password. Is this possible? Here is what I'm using to execute the csh script:
import commands
commands.getoutput('server stop')
Have a look at the pexpect module. It is designed to deal with interactive programs, which seems to be your case.
Oh, and remember that hard-encoding root's password in a shell or python script is potentially a security hole :D
Use subprocess. Call Popen() to create your process and use communicate() to send it text. Sorry, forgot to include the PIPE..
from subprocess import Popen, PIPE
proc = Popen(['server', 'stop'], stdin=PIPE)
proc.communicate('password')
You would do better do avoid the password and try a scheme like sudo and sudoers. Pexpect, mentioned elsewhere, is not part of the standard library.
import pexpect
child = pexpect.spawn('server stop')
child.expect_exact('Password:')
child.sendline('password')
print "Stopping the servers..."
index = child.expect_exact(['Server processes successfully stopped.', 'Server is not running...'], 60)
child.expect(pexpect.EOF)
Did the trick! Pexpect rules!
Add input= in proc.communicate() make it run, for guys who like to use standard lib.
from subprocess import Popen, PIPE
proc = Popen(['server', 'stop'], stdin=PIPE)
proc.communicate(input='password')
Should be able to pass it as a parameter. something like:
commands.getoutput('server stop -p password')
This seems to work better:
import popen2
(stdout, stdin) = popen2.popen2('server stop')
stdin.write("password")
But it's not 100% yet. Even though "password" is the correct password I'm still getting su: Sorry back from the csh script when it's trying to su to root.
To avoid having to answer the Password question in the python script I'm just going to run the script as root. This question is still unanswered but I guess I'll just do it this way for now.