从Python调用外部命令

伽罗 · 07月28日

您如何在Python脚本中调用外部命令(就像我在Unix Shell或Windows命令提示符下键入的一样)?

回答(12)
时间
N
Near
12 · 2020-07-28 11:18:56

查看标准库中的子流程模块:

import subprocess
subprocess.run(["ls", "-l"])

的优势subprocess主场迎战system的是,它是更灵活(你可以得到的stdoutstderr,“真正”的状态代码,更好的错误处理,等等)。

官方文件建议subprocess在替代模块os.system()

subprocess模块提供了更强大的功能来生成新流程并检索其结果。使用该模块优于使用此功能[ os.system()]。

与子模块更换旧的功能中部分subprocess文件可能有一些有益的食谱。

对于3.5之前的Python版本,请使用call

import subprocess
subprocess.call(["ls", "-l"])
神离
11 · 2020-07-28 11:18:56

在Windows中你可以导入subprocess模块,并通过调用运行外部命令subprocess.Popen()subprocess.Popen().communicate()subprocess.Popen().wait()如下:

# Python script to run a command line
import subprocess

def execute(cmd):
    """
        Purpose  : To execute a command and return exit status
        Argument : cmd - command to execute
        Return   : exit_code
    """
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    (result, error) = process.communicate()

    rc = process.wait()

    if rc != 0:
        print "Error: failed to execute command:", cmd
        print error
    return result
# def

command = "tasklist | grep python"
print "This process detail: \n", execute(command)

输出:

This process detail:
python.exe                     604 RDP-Tcp#0                  4      5,660 K
鱼二水
10 · 2020-07-28 11:18:56

subprocess.check_call如果您不想测试返回值,则非常方便。任何错误都会引发异常。

西
西里
9 · 2020-07-28 11:18:56

我很喜欢shell_command的简单性。它建立在子流程模块的顶部。

这是文档中的示例:

>>> from shell_command import shell_call
>>> shell_call("ls *.py")
setup.py  shell_command.py  test_shell_command.py
0
>>> shell_call("ls -l *.py")
-rw-r--r-- 1 ncoghlan ncoghlan  391 2011-12-11 12:07 setup.py
-rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py
-rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py
0
8 · 2020-07-28 11:18:56

os.system不允许您存储结果,因此,如果要将结果存储在某个列表或某些内容中,则subprocess.call可以使用。

伽罗
7 · 2020-07-28 11:18:56

这里还有另一个以前没有提到的区别。

subprocess.Popen将<command>作为子进程执行。就我而言,我需要执行文件<a>,该文件需要与另一个程序<b>通信。

我尝试了子流程,执行成功。但是<b>无法与<a>通信。当我从终端运行时,一切都正常。

还有一个:(注意:kwrite的行为与其他应用程序不同。如果在Firefox上尝试以下操作,结果将有所不同。)

如果尝试这样做os.system("kwrite"),程序流将冻结,直到用户关闭kwrite。为了克服这个问题,我改为尝试os.system(konsole -e kwrite)这个时间程序继续进行,但是kwrite成为了控制台的子进程。

任何人都运行kwrite而不是子进程(即,它必须在系统监视器中显示在树的最左侧)。

I
Itachi
6 · 2020-07-28 11:18:56

下面总结了调用外部程序的方法以及每种方法的优缺点:

  1. os.system("some_command with args")将命令和参数传递到系统的外壳程序。很好,因为您实际上可以以这种方式一次运行多个命令,并设置管道和输入/输出重定向。例如:

    os.system("some_command < input_file | another_command > output_file")  

但是,尽管这很方便,但是您必须手动处理外壳字符(例如空格等)的转义。另一方面,这还允许您运行仅是外壳命令而不是外部程序的命令。请参阅文档

  1. stream = os.popen("some_command with args")os.system除了会为您提供类似于文件的对象之外,您可以使用该对象来访问该过程的标准输入/输出,它的作用与之相同popen还有其他3种变体,它们对I / O的处理略有不同。如果您将所有内容都作为字符串传递,那么您的命令将传递到外壳程序;如果将它们作为列表传递,则无需担心转义任何内容。请参阅文档

  2. 模块Popensubprocess它旨在替代它,os.popen但缺点是由于太全面而使它稍微复杂一些。例如,您会说:

    print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()

    代替:

    print os.popen("echo Hello World").read()

    但是将所有选项都放在一个统一的类中而不是4个不同的popen函数是一个很好的选择。请参阅文档

  3. call来自subprocess模块功能基本上,这和Popen该类一样,并使用所有相同的参数,但是它只是等待命令完成并提供返回代码。例如:

    return_code = subprocess.call("echo Hello World", shell=True)  

    请参阅文档

  4. 如果您使用的是Python 3.5或更高版本,则可以使用新subprocess.run函数,该函数与上面的代码非常相似,但是更加灵活,并CompletedProcess在命令完成执行后返回一个对象。

  5. os模块还具有您在C程序中拥有的所有fork / exec / spawn函数,但是我不建议直接使用它们。

subprocess模块可能是您所使用模块。

最后,请注意,对于所有方法,在这些方法中,您将要由外壳程序执行的最终命令作为字符串传递给您,并且您有责任对其进行转义。如果您传递的字符串的任何部分不能被完全信任,则将带来严重的安全隐患例如,如果用户正在输入字符串的某些/任何部分。如果不确定,请仅将这些方法与常量一起使用。为了给您暗示的含义,请考虑以下代码:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

并想象用户输入了“我的妈妈不爱我&& rm -rf /”这样的东西,它可能会擦除整个文件系统。

三千曜
5 · 2020-07-28 11:18:56

可能很简单:

import os
cmd = "your command"
os.system(cmd)
E
Elena
4 · 2020-07-28 11:18:55

os.system可以,但是有点过时。这也不是很安全。相反,请尝试subprocesssubprocess不会直接调用sh,因此比os.system

在此处获取更多信息

理查德
3 · 2020-07-28 11:18:54

这就是我运行命令的方式。这段代码包含了您非常需要的所有内容

from subprocess import Popen, PIPE
cmd = "ls -l ~/"
p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE)
out, err = p.communicate()
print "Return code: ", p.returncode
print out.rstrip(), err.rstrip()
小胖
2 · 2020-07-28 11:18:54

关于从调用者中分离子进程的一些提示(在后台启动子进程)。

假设您要从CGI脚本开始一个长任务。也就是说,子进程的生存期应比CGI脚本执行进程的生存期长。

子流程模块文档中的经典示例是:

import subprocess
import sys

# Some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # Call subprocess

# Some more code here

这里的想法是,在longtask.py完成之前,您不希望在“调用子进程”行中等待。但是尚不清楚示例中“这里有更多代码”行之后会发生什么。

我的目标平台是FreeBSD,但是开发是在Windows上进行的,因此我首先在Windows上遇到了问题。

在Windows(Windows XP)上,直到longtask.py完成工作后,父进程才会完成。这不是CGI脚本中想要的。这个问题不是特定于Python的。在PHP社区中,问题是相同的。

解决方案是将DETACHED_PROCESS 进程创建标志传递给Windows API中的基础CreateProcess函数。如果碰巧安装了pywin32,则可以从win32process模块​​中导入该标志,否则您应该自己定义它:

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

/ * UPD 2015.10.27 @eryksun在下面的注释中指出,语义正确的标志是CREATE_NEW_CONSOLE(0x00000010)* /

在FreeBSD上,我们还有另一个问题:父进程完成后,它也会完成子进程。那也不是您在CGI脚本中想要的。一些实验表明,问题似乎出在共享sys.stdout中。可行的解决方案如下:

pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

我还没有检查其他平台上的代码,也不知道FreeBSD上该行为的原因。如果有人知道,请分享您的想法。谷歌搜索在Python中启动后台进程尚未透露任何信息。

T
TonyEva
1 · 2020-07-28 11:18:54
import os
os.system("your command")

请注意,这很危险,因为未清除命令。我留给你去谷歌搜索有关“操作系统”和“系统”模块的相关文档。有很多函数(exec *和spawn *)将执行类似的操作。

你的回答

加载中...
⌘+Return 发表
发表

温馨提示:登录后可发表评论或回复

关闭,朕知道了

扫码关注微信公众号或小程序