Python 如何使subprocess.Popen正常工作?
我正在尝试使Python 如何使subprocess.Popen正常工作?,python,python-2.7,subprocess,popen,Python,Python 2.7,Subprocess,Popen,我正在尝试使subprocess.Popen()正常工作,但由于某些原因,返回的值完全错误 该脚本打开从服务器下载文件的FTP连接脚本,然后返回成功下载和未成功下载文件的元组。这个脚本在使用subprocess.call()之前已经工作过,但是我想使用Popen(),这样它调用的脚本就在另一个线程中,不会干扰主程序 这是我的主要课程: def FTPDownload(self): try: ftpReq = subprocess.Popen(['Python', mw._
subprocess.Popen()
正常工作,但由于某些原因,返回的值完全错误
该脚本打开从服务器下载文件的FTP连接脚本,然后返回成功下载和未成功下载文件的元组。这个脚本在使用subprocess.call()
之前已经工作过,但是我想使用Popen()
,这样它调用的脚本就在另一个线程中,不会干扰主程序
这是我的主要课程:
def FTPDownload(self):
try:
ftpReq = subprocess.Popen(['Python', mw._['cwd']+"dwnldMedia.py"],
shell=True,
stdout=subprocess.PIPE)
successful, unsuccessful = ftpReq.communicate()
self.consPrompt("Successful:\t"+str(successful))
self.consPrompt("Unsuccessful:\t"+str(unsuccessful))
except subprocess.CalledProcessError as e:
self.consPrompt((cp._['E0']).format(str(e)))
这里是dwnldMedia.py
(调用下载()
):
我得到的结果是:
成功:
不成功:无
如果successful
的值为None
如果您确实使用了一些东西,那么您最好继续使用它-因为call()
在内部使用Popen()
,所以dwnldMedia.py
已经作为一个单独的子进程运行了(您称之为新线程),这样代码执行的这一方面就不会因为直接调用Popen()
而改变
无论您使用call()
还是Popen()
+communicate()
下载都不会同时进行(我假设这是您的目标),因为两者都会等待脚本完成执行后再继续。对于并发下载,您需要使用多处理
模块执行多任务。由于您所做的是I/O绑定的,因此也可以使用线程
和/或线程
模块来完成并发下载(在这些模块中,共享数据通常比较简单,因为数据都在同一进程中)
话虽如此,这实际上是对您问题的回答,下面是如何使用从subprocess.communicate()
返回的结果,并将数据从一个进程传递到另一个进程。您不能简单地将结果从一个进程返回到另一个进程,因为它们位于不同的地址空间中。一种方法是在它们之间“管道化”数据communicate()
收集接收到的所有数据,并在返回时将其作为两个字符串的元组返回,一个用于stderr
,另一个用于stderr
该示例使用pickle
将发送的数据转换为可以在接收端的Python对象中转回的内容。json
模块同样运行良好。我不得不从您问题中的示例中删除大量代码,以制作一些可以运行和测试的东西,但我已尝试在下面的内容中保持整体结构的完整性
import cPickle as pickle
import subprocess
class SomeClass(object):
def FTPDownload(self):
try:
# The -u argument puts stdin, stdout and stderr into binary mode
# (as well an makes them unbuffered). This is needed to avoid
# an issue with writing pickle data to streams in text mode
# on Windows.
ftpReq = subprocess.Popen(['python', '-u', 'dwnldMedia.py'],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = ftpReq.communicate()
if stdout:
# convert object returned into a Python obj
results = pickle.loads(stdout)
print(' successful: {successful}'.format(**results))
print('unsuccessful: {unsuccessful}'.format(**results))
if stderr:
print("stderr:\n{}".format(stderr))
except subprocess.CalledProcessError as exception:
print('exception: {}'.format(str(exception)))
if __name__ == '__main__':
instance = SomeClass()
instance.FTPDownload()
下面是dwnldMedia.py
脚本中的download()
方法的精简版本:
import cPickle as pickle
from random import randint # for testing
import os
# needed if not run in -u mode
#if os.name == 'nt': # put stdout into binary mode on Windows
# import sys, msvcrt
# msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
class OtherClass(object):
def __init__(self, files):
self.files = files
self.download()
def download(self):
files = [fn for fn in self.files if fn != '.' and fn != '..']
successful = []
unsuccessful = []
for fn in files:
if randint(0, 1) % 2: # simulate random download success
successful.append(fn)
for fn in files:
if fn not in successful:
unsuccessful.append(fn)
results = { # package lists into single object
'successful': successful,
'unsuccessful': unsuccessful
}
print(pickle.dumps(results)) # send object by writing it to stdout
instance = OtherClass(['.', '..', 'file1', 'file2', 'file3', 'file4'])
从进程返回stdout
和stderr
的内容,而不是download()
方法返回的内容。换句话说,您需要将successful
和successful
的值写入sys.stdout
。一种方法是将它们打印出来。stderr=subprocess.PIPE
如果您希望错误流中有任何数据,我建议您添加一个stderr=subprocess.PIPE
。@martineau,请将您的答案作为答案发布。这不是“另一个线程”,而是一个子流程。噢。。我觉得这就像打开了一条新的线。您可以使用哪个库打开新线程?我尝试过使用多处理,但没有运行目标脚本,它只是复制了我的GUI窗口ftp=multiprocessing.Process(name=“ftp download”,target=mw.\u['cwd']+“dwnldMedia.py”)
(其中mw.['cwd']是当前的工作目录)多处理
可能很棘手。使用它的规则之一——听起来你可能没有遵循——是主脚本必须有一个如果uuuu name_uuuu=='uuuuu main_uuu':
围绕表示根进程的代码部分进行保护(因为主脚本是由子进程导入的import
)。
import cPickle as pickle
from random import randint # for testing
import os
# needed if not run in -u mode
#if os.name == 'nt': # put stdout into binary mode on Windows
# import sys, msvcrt
# msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
class OtherClass(object):
def __init__(self, files):
self.files = files
self.download()
def download(self):
files = [fn for fn in self.files if fn != '.' and fn != '..']
successful = []
unsuccessful = []
for fn in files:
if randint(0, 1) % 2: # simulate random download success
successful.append(fn)
for fn in files:
if fn not in successful:
unsuccessful.append(fn)
results = { # package lists into single object
'successful': successful,
'unsuccessful': unsuccessful
}
print(pickle.dumps(results)) # send object by writing it to stdout
instance = OtherClass(['.', '..', 'file1', 'file2', 'file3', 'file4'])