在Python脚本中启动一个shell命令,等待其执行完毕并返回到脚本。

82

我有一个Python脚本,需要为目录中的每个文件启动一个shell命令:

import os

files = os.listdir(".")
for f in files:
    os.execlp("myscript", "myscript", f)

这对于第一个文件很好用,但是在“myscript”命令结束后,执行停止并且不会返回到Python脚本。

我该怎么做?我需要在调用os.execlp()之前fork()吗?


returncode = subprocess.call(['myscript', f]) 返回代码 = subprocess.call(['myscript',f]) - jfs
7个回答

128

subprocess: subprocess 模块允许你生成新的进程,连接到它们的输入/输出/错误管道,并获取它们的返回代码。

http://docs.python.org/library/subprocess.html

用法:

import subprocess
process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
process.wait()
print process.returncode

1
我认为作为被接受的答案,这应该至少包含与@Harley相同数量的细节。这更像是个人请求,但我认为新版本文档呈现了更好的信息。你能链接到2.7版本的文档吗? - Ehtesh Choudhury
3
文档的更新版本位于 http://docs.python.org/library/subprocess.html。 - wanderso
4
移除 stdout=PIPE,因为如果子程序产生足够的输出(大约在Linux上达到65KB),它可能会挂起脚本。shell=True是不必要的。 - jfs
2
如果没有使用 shell=True,在 Python 2.7 上会引发异常。File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/subprocess.py", line 1335, in _execute_child raise child_exception OSError: [Errno 2] No such file or directory - SeF

66

你可以使用subprocess.Popen。有几种方法可以实现:


import subprocess
cmd = ['/run/myscript', '--arg', 'value']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
for line in p.stdout:
    print line
p.wait()
print p.returncode

或者,如果您不关心外部程序实际执行的操作:

cmd = ['/run/myscript', '--arg', 'value']
subprocess.Popen(cmd).wait()

1
for line in p.stdout: 可能会因为预读而延迟输出一行。您可以使用 for line in iter(p.stdout.readline, b''): print line,(注意:末尾有逗号)。在第二个示例中删除 stdout=PIPE;它可能会挂起脚本。或者,如果您不需要非零返回代码的异常,则只需使用 subprocess.call() - jfs
@HarleyHolcombe,我试了你的解决方案,使用 subprocess.Popen("/usr/bin/sleep 20").wait() ,但是无法正常工作(FileNotFoundError:)。为了让它正常工作,我必须将命令分解为子字符串并将其放入列表中:subprocess.Popen(["/usr/bin/sleep", "20"]).wait()。为什么需要这样做呢? - Alexander Cska

13

自2008年以来,子进程模块已经取得了长足的发展。特别是check_callcheck_output使得简单的子进程操作更加容易。 check_*函数族很好用,如果出现问题,它们会引发异常。

import os
import subprocess

files = os.listdir('.')
for f in files:
   subprocess.check_call( [ 'myscript', f ] )

任何由myscript生成的输出都将显示为您的进程产生的输出(严格来说,myscript和您的python脚本共享相同的stdout)。避免这种情况有几种方法。
  • check_call(['myscript', f], stdout=subprocess.PIPE)
    stdout将被抑制(如果myscript产生超过4k的输出,则需注意)。除非添加选项stderr=subprocess.PIPE,否则仍会显示stderr。
  • check_output(['myscript', f])
    check_output返回stdout作为字符串,因此不会显示。除非添加选项stderr=subprocess.STDOUT,否则仍会显示stderr。

4
@Luke Stanley:你的问题不太清楚,但我会尝试回答。subprocess.check_*函数使得调用外部二进制文件比手动使用subprocess.Popen更加容易。特别是你不需要处理wait()communicate()return_code等等。你也不需要担心诸如悬空管道阻塞可执行文件之类的陷阱。当涉及与外部二进制文件进行交互时,check_*函数并不特别有用,但对于大多数用例而言,结果更加简短且更少出错,即更容易操作。 - deft_code
1
你可以使用 subprocess.DEVNULL 或者 open(os.devnull, 'r+b') 来忽略输入/输出,而不是使用 subprocess.PIPE,以避免可能的死锁。 - jfs

7
< p > os.exec*() 函数会替代当前程序并启动新的程序。当新程序结束时,你的进程也将结束。你可能需要使用 os.system()


1
os.system()在安全方面存在很大问题,以至于许多C库直接将此标准C函数删除。应改用带有shell=False的subprocess.Popen()。 - Shnatsel

3

使用 spawn

import os
os.spawnlp(os.P_WAIT, 'cp', 'cp', 'index.html', '/dev/null')

2
我使用os.system。
import os
os.system("pdftoppm -png {} {}".format(path2pdf, os.path.join(tmpdirname, "temp")))

1
This worked well for me!

shell_command = "ls -l" subprocess.call(shell_command.split())


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接