如何让 subprocess.Popen 正常工作?
How to get subprocess.Popen to work correctly?
我正在尝试让 subprocess.Popen()
正常工作,但由于某种原因返回的值是完全错误的。
该脚本打开一个 FTP 从服务器下载文件的连接脚本,然后 returns 一个包含成功和未成功下载文件的元组。该脚本在使用 subprocess.call()
之前可以正常工作,但我想使用 Popen()
以便它调用的脚本位于另一个线程中并且不会干扰主程序。
这是我的主要内容 class:
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
(__init__
调用 download()
):
def download(self):
#print("connected")
self.server = FTP(**self.serverDetails)
self.server.login(**self.userDetails)
self.server.cwd("/public_html/uploads") #changing to /pub/unix
#print "File List: \n"
files = []
successful = [0]
unsuccessful = [0]
self.server.retrlines("NLST",files.append)
for f in files:
if(f != '.' and f != '..'):
#print("downloading:\t"+f)
local_filename = os.path.join(mw._['cwd']+"media", f)
with open(local_filename, "wb") as i:
self.server.retrbinary("RETR " + f, i.write)
#print("\t| Success")
successful.append(f)
for f in files:
if(f != '.' and f != '..' and f not in successful):
unsuccessful.append(f)
return (successful, unsuccessful)
我得到的输出是:
Successful:
Unsuccessful: None
其中 successful
的值为 None
。
如果您确实有使用 subprocess.call()
的东西,您不妨继续使用它 — 因为 call()
在内部使用 Popen()
— 所以 dwnldMedia.py
是 已经 运行 作为一个单独的子进程(你所谓的新线程),所以代码执行的这方面不会通过直接让您的代码调用 Popen()
来更改。
无论您使用 call()
还是 Popen()
+communicate()
下载都不会同时发生(我假设这是您的目标),因为两者都等待脚本完成执行后再继续.对于并发下载,您需要使用 multiprocessing
模块进行多任务处理。由于您正在做的是 I/O 绑定,并发下载也可以使用 thread
and/or threading
模块完成(共享数据通常更简单,因为它是所有在同一进程内)。
话虽如此,这实际上是对您问题的回答,这里介绍了如何使用从 subprocess.communicate()
返回的结果并将数据从一个进程传递到另一个进程。您不能简单地将 return
结果从一个进程传递到另一个进程,因为它们位于不同的地址空间中。一种方法是 "pipe" 它们之间的数据。 communicate()
收集所有收到的数据,returns 当它 returns 时,它作为两个字符串的元组,一个用于 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'])
我正在尝试让 subprocess.Popen()
正常工作,但由于某种原因返回的值是完全错误的。
该脚本打开一个 FTP 从服务器下载文件的连接脚本,然后 returns 一个包含成功和未成功下载文件的元组。该脚本在使用 subprocess.call()
之前可以正常工作,但我想使用 Popen()
以便它调用的脚本位于另一个线程中并且不会干扰主程序。
这是我的主要内容 class:
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
(__init__
调用 download()
):
def download(self):
#print("connected")
self.server = FTP(**self.serverDetails)
self.server.login(**self.userDetails)
self.server.cwd("/public_html/uploads") #changing to /pub/unix
#print "File List: \n"
files = []
successful = [0]
unsuccessful = [0]
self.server.retrlines("NLST",files.append)
for f in files:
if(f != '.' and f != '..'):
#print("downloading:\t"+f)
local_filename = os.path.join(mw._['cwd']+"media", f)
with open(local_filename, "wb") as i:
self.server.retrbinary("RETR " + f, i.write)
#print("\t| Success")
successful.append(f)
for f in files:
if(f != '.' and f != '..' and f not in successful):
unsuccessful.append(f)
return (successful, unsuccessful)
我得到的输出是:
Successful:
Unsuccessful: None
其中 successful
的值为 None
。
如果您确实有使用 subprocess.call()
的东西,您不妨继续使用它 — 因为 call()
在内部使用 Popen()
— 所以 dwnldMedia.py
是 已经 运行 作为一个单独的子进程(你所谓的新线程),所以代码执行的这方面不会通过直接让您的代码调用 Popen()
来更改。
无论您使用 call()
还是 Popen()
+communicate()
下载都不会同时发生(我假设这是您的目标),因为两者都等待脚本完成执行后再继续.对于并发下载,您需要使用 multiprocessing
模块进行多任务处理。由于您正在做的是 I/O 绑定,并发下载也可以使用 thread
and/or threading
模块完成(共享数据通常更简单,因为它是所有在同一进程内)。
话虽如此,这实际上是对您问题的回答,这里介绍了如何使用从 subprocess.communicate()
返回的结果并将数据从一个进程传递到另一个进程。您不能简单地将 return
结果从一个进程传递到另一个进程,因为它们位于不同的地址空间中。一种方法是 "pipe" 它们之间的数据。 communicate()
收集所有收到的数据,returns 当它 returns 时,它作为两个字符串的元组,一个用于 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'])