subprocess.Popen() 替代 os.system() 的问题

1 投票
3 回答
3584 浏览
提问于 2025-04-17 20:02

我正在一个有48个核心的集群上运行一个Python脚本,这个集群实际上有一个主机和三个从机。我需要生成一组文件,运行一些脚本来处理这些文件,收集结果,然后删除这些文件。接着我会重复这个过程——重新生成文件、执行脚本、删除文件等等。

当我删除并重新生成同名的文件时,我发现从机会抱怨找不到这些文件。

我通过os.system()来运行Python脚本。

我从这篇文章了解到,使用subprocess.Popen()os.system更好,因为这样可以确保我的脚本生成文件后再继续执行。我可以使用os.system("pause")或者time.sleep(whatever)来等待,但我想把我的os.system换成subprocess.popens或者subprocess.calls,现在卡住了。

我查阅了Python的文档,尝试了subprocess.Popen('ls'),但我无法让像subprocess.Popen('cd /whatever_directory')这样简单的命令正常工作。

这听起来可能有点傻,但我想知道如何通过subprocess来执行像cd这样的简单命令,而不是用os.system('cd')

然后,我实际上想把以下内容转换成subprocess。我该怎么做呢?

import os,optparse
from optparse import OptionParser

parser.add_option("-m", "--mod",dest='module', help='Enter the entity name')
parser.add_option("-f", "--folder", dest="path",help="Enter the path")

module=options.module
path=options.path

os.system('python %s/python_repeat_deckgen_remote.py -m %s' %(path,module))

我只是把os.system替换成了subprocess.Popen。

但这给了我很多错误提示:

File "/usr/lib64/python2.6/subprocess.py", line 633, in __init__
    errread, errwrite)
  File "/usr/lib64/python2.6/subprocess.py", line 1139, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory

3 个回答

0

我可能没有直接回答你的问题,但对于需要运行子进程的任务,我总是使用 plumbum

在我看来,这样做会让任务变得简单且更容易理解,包括在远程机器上运行。

使用 plumbum 时,如果想要设置子进程的工作目录,你可以在 with local.cwd(path): my_cmd() 的上下文中运行命令。

2

我无法让像 subprocess.Popen('cd /whatever_directory') 这样简单的命令正常工作。

每个进程的当前工作目录都是独立的,和系统中的其他进程没有关系。

当你启动一个子进程(使用这些方法中的任何一种),让它 cd 到另一个目录时,这对父进程没有影响,也就是对你的Python脚本没有影响。

要改变你脚本的当前工作目录,你应该使用 os.chdir()

2

正如NPE已经提到的,新创建的进程不会影响到已经存在的进程(这意味着 os.system('cd /some/where') 对当前进程也没有影响)。不过在这种情况下,我觉得你可能被一个细节绊住了,那就是 os.system 会启动一个命令行来解释你传入的命令,而 subprocess.Popen 默认情况下并不会这样做。但你可以告诉它这样做:

proc = subprocess.Popen(
    'python %s/python_repeat_deckgen_remote.py -m %s' % (path, module),
    shell = True)
status = proc.wait()

如果你要调用一个命令行内置的命令或者使用命令行扩展,那么就必须启动命令行(前提是你不想模拟这个过程):

>>> import subprocess
>>> x = subprocess.Popen('echo $$')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python2.7/subprocess.py", line 679, in __init__
    errread, errwrite)
  File "/usr/local/lib/python2.7/subprocess.py", line 1249, in _execute_child
    raise child_exception
OSError: [Errno 2] No such file or directory
>>> x = subprocess.Popen('echo $$', shell = True); x.wait()
81628
0
>>> 

不过,如果你的问题允许的话,你可以绕过命令行——这对安全性有帮助——通过把命令的参数拆分成一个列表:

>>> x = subprocess.Popen(['echo', '$$']); x.wait()
$$
0
>>>

注意这次输出的是字符串 $$,而不是进程ID,因为这次命令行没有解释这个字符串。

比如对于你最初的例子,你可以使用:

proc = subprocess.Popen(['python',
    os.path.join(path, 'python_repeat_deckgen_remote.py'),
    '-m',
    module])

这样可以避免如果 path 和/或 module 包含命令行特殊字符时出现的问题。

(你甚至可以考虑用 cwd = path 来调用 subprocess.Popen,这样就可以省去调用 os.path.join,不过这要看其他情况。)

撰写回答