首页 文章

在Python中调用外部命令

提问于
浏览
3892

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

30 回答

  • 46

    如果您需要调用的命令的输出,则可以使用subprocess.check_output(Python 2.7) .

    >>> subprocess.check_output(["ls", "-l", "/dev/null"])
    'crw-rw-rw- 1 root root 1, 3 Oct 18  2007 /dev/null\n'
    

    另请注意shell参数 .

    如果shell为True,则将通过shell执行指定的命令 . 如果您主要使用Python来提供它在大多数系统shell上提供的增强控制流,并且仍然希望方便地访问其他shell功能,例如shell管道,文件名通配符,环境变量扩展以及扩展到用户家中,这将非常有用 . 目录 . 但请注意,Python本身提供了许多类似shell的功能的实现(特别是glob,fnmatch,os.walk(),os.path.expandvars(),os.path.expanduser()和shutil) .

  • 40

    https://docs.python.org/2/library/subprocess.html

    ......或者是一个非常简单的命令:

    import os
    os.system('cat testfile')
    
  • 58
    import os
    os.system("your command")
    

    请注意,这是危险的,因为未清除该命令 . 我把它留给你去谷歌搜索关于'os'和'sys'模块的相关文档 . 有许多函数(exec *和spawn *)可以执行类似的操作 .

  • 109

    没有结果的输出:

    import os
    os.system("your command here")
    

    输出结果:

    import commands
    commands.getoutput("your command here")
    or
    commands.getstatusoutput("your command here")
    
  • 17

    os.system 不允许您存储结果,因此如果您想将结果存储在某个列表或某些内容中, subprocess.call 可以正常工作 .

  • 2593

    这里有另一个不同之处,前面没有提到过 .

    subprocess.Popen 将<command>作为子进程执行 . 在我的例子中,我需要执行需要与另一个程序进行通信的文件<a> <b> .

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

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

    如果您尝试 os.system("kwrite") ,程序流将冻结,直到用户关闭kwrite . 为了克服这一点,我尝试了 os.system(konsole -e kwrite) . 这个时间程序继续流动,但kwrite成为控制台的子进程 .

    任何人都运行kwrite不是一个子进程(即在系统监视器中它必须出现在树的最左边) .

  • 43

    你可以使用Popen,然后你可以检查程序的状态:

    from subprocess import Popen
    
    proc = Popen(['ls', '-l'])
    if proc.poll() is None:
        proc.kill()
    

    看看subprocess.Popen .

  • 13

    有关将子进程与调用进程分离的一些提示(在后台启动子进程) .

    假设您想从CGI脚本启动一个长任务,即子进程应该比CGI脚本执行过程更长寿 .

    子流程模块docs的经典示例是:

    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(win xp)上,父进程将不会完成,直到longtask.py完成其工作 . 这不是你想要的CGI脚本 . 问题不是Python特有的,在PHP社区中问题是一样的 .

    解决方案是将DETACHED_PROCESS Process Creation Flag传递给win 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中启动后台进程尚未解决任何问题 .

  • 18

    它可以很简单:

    import os
    cmd = "your command"
    os.system(cmd)
    
  • 110
    import os
    cmd = 'ls -al'
    os.system(cmd)
    

    如果要返回命令的结果,可以使用os.popen . 但是,从版本2.6开始,这已经被弃用了subprocess module,其他答案已经很好地解决了 .

  • 17

    我非常喜欢shell_command,因为它简单 . 它 Build 在子进程模块之上 .

    以下是文档中的示例:

    >>> 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
    
  • 25

    在Python中调用外部命令

    简单,使用 subprocess.run ,它返回一个 CompletedProcess 对象:

    >>> import subprocess
    >>> completed_process = subprocess.run('python --version')
    Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
    >>> completed_process
    CompletedProcess(args='python --version', returncode=0)
    

    为什么?

    从Python 3.5开始,文档建议subprocess.run

    调用子进程的推荐方法是对它可以处理的所有用例使用run()函数 . 对于更高级的用例,可以直接使用底层的Popen接口 .

    这是一个最简单的使用示例 - 它确实如此完全如我所说:

    >>> import subprocess
    >>> completed_process = subprocess.run('python --version')
    Python 3.6.1 :: Anaconda 4.4.0 (64-bit)
    >>> completed_process
    CompletedProcess(args='python --version', returncode=0)
    

    run 等待命令成功完成,然后返回 CompletedProcess 对象 . 它可能会提升 TimeoutExpired (如果你给它一个 timeout= 参数)或 CalledProcessError (如果它失败并且你传递 check=True ) .

    正如您可能从上面的示例中推断的那样,默认情况下,stdout和stderr都会通过管道输出到您自己的stdout和stderr .

    我们可以检查返回的对象并查看给出的命令和返回码:

    >>> completed_process.args
    'python --version'
    >>> completed_process.returncode
    0
    

    捕获输出

    如果要捕获输出,可以将 subprocess.PIPE 传递给相应的 stderrstdout

    >>> cp = subprocess.run('python --version', 
                            stderr=subprocess.PIPE, 
                            stdout=subprocess.PIPE)
    >>> cp.stderr
    b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
    >>> cp.stdout
    b''
    

    (我觉得有趣且有点违反直觉,版本信息被放到stderr而不是stdout . )

    传递命令列表

    人们可以轻松地从手动提供命令字符串(如问题建议)到提供以编程方式构建的字符串 . Don't build strings programmatically. 这是一个潜在的安全问题 . 它's better to assume you don'吨信任输入 .

    >>> import textwrap
    >>> args = ['python', textwrap.__file__]
    >>> cp = subprocess.run(args, stdout=subprocess.PIPE)
    >>> cp.stdout
    b'Hello there.\r\n  This is indented.\r\n'
    

    注意,只有 args 应该在位置上传递 .

    完整签名

    这是源中的实际签名,如 help(run) 所示:

    def run(* popenargs,input = None,timeout = None,check = False,** kwargs):

    popenargskwargs 被赋予 Popen 构造函数 . input 可以是一串字节(或unicode,如果指定编码或 universal_newlines=True ),它将被传送到子进程的stdin .

    该文档描述了 timeout=check=True 比我更好:

    timeout参数传递给Popen.communicate() . 如果超时到期,子进程将被终止并等待 . 子进程终止后,将重新引发TimeoutExpired异常 . 如果check为true,并且进程以非零退出代码退出,则将引发CalledProcessError异常 . 该异常的属性包含参数,退出代码以及stdout和stderr(如果它们被捕获) .

    这个 check=True 的例子比我想出的更好:

    subprocess.run(“exit 1”,shell = True,check = True)
    回溯(最近的呼叫最后):
    ...
    subprocess.CalledProcessError:命令'exit 1'返回非零退出状态1

    扩展签名

    这是一个扩展的签名,如文档中所示:

    subprocess.run(args,*,stdin = None,input = None,stdout = None,stderr = None,
    shell = False,cwd = None,timeout = None,check = False,encoding = None,
    错误=无)

    请注意,这表示只应按位置传递args列表 . 因此将剩余的参数作为关键字参数传递 .

    Popen

    当使用 Popen 时?我很难根据论据单独找到用例 . 但是,直接使用 Popen 可以访问其方法,包括 poll ,'send_signal','terminate'和'wait' .

    这是the source中给出的 Popen 签名 . 我认为这是对信息的最精确封装(与 help(Popen) 相对):

    def __init__(self, args, bufsize=-1, executable=None,
                 stdin=None, stdout=None, stderr=None,
                 preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
                 shell=False, cwd=None, env=None, universal_newlines=False,
                 startupinfo=None, creationflags=0,
                 restore_signals=True, start_new_session=False,
                 pass_fds=(), *, encoding=None, errors=None):
    

    但更多信息是the Popen documentation

    subprocess.Popen(args,bufsize = -1,executable = None,stdin = None,
    stdout = None,stderr = None,preexec_fn = None,close_fds = True,
    shell = False,cwd = None,env = None,universal_newlines = False,
    startupinfo = None,creationflags = 0,restore_signals = True,
    start_new_session = False,pass_fds =(),*,encoding = None,errors = None)
    在新进程中执行子程序 . 在POSIX上,该类使用os.execvp() - 类似行为来执行子程序 . 在Windows上,该类使用Windows CreateProcess()函数 . Popen的论点如下 .

    了解 Popen 上的其余文档将留给读者练习 .

  • 282

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

    # 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
    
  • 26

    使用标准库

    使用subprocess module(Python 3):

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

    这是推荐的标准方式 . 但是,更复杂的任务(管道,输出,输入等)构造和写入可能很繁琐 .

    关于Python版本的注意事项:如果您仍在使用Python 2,subprocess.call的工作方式类似 .

    ProTip:shlex.split可以帮助你解析 runcall 和其他 subprocess 函数的命令,以防你't want (or you can' t!)以列表的形式提供它们:

    import shlex
    import subprocess
    subprocess.run(shlex.split('ls -l'))
    

    具有外部依赖关系

    如果您不介意外部依赖项,请使用plumbum

    from plumbum.cmd import ifconfig
    print(ifconfig['wlan0']())
    

    它是最好的 subprocess 包装器 . 它是跨平台的,即它适用于Windows和类Unix系统 . 按 pip install plumbum 安装 .

    另一个受欢迎的图书馆是sh

    from sh import ifconfig
    print(ifconfig('wlan0'))
    

    但是, sh 放弃了Windows支持,所以它不像过去那样棒 . 按 pip install sh 安装 .

  • 22

    我倾向于将subprocessshlex一起使用(以处理引用字符串的转义):

    >>> import subprocess, shlex
    >>> command = 'ls -l "/your/path/with spaces/"'
    >>> call_params = shlex.split(command)
    >>> print call_params
    ["ls", "-l", "/your/path/with spaces/"]
    >>> subprocess.call(call_params)
    
  • 14

    更新:

    subprocess.run 是推荐的方法as of Python 3.5,如果您的代码不需要保持与早期Python版本的兼容性 . 尽管如此,'s more consistent and offers similar ease-of-use as Envoy. (Piping isn' t直截了当 . 见this question for how . )

    以下是the docs的一些例子 .

    运行一个过程:

    >>> subprocess.run(["ls", "-l"])  # doesn't capture output
    CompletedProcess(args=['ls', '-l'], returncode=0)
    

    提高失败的运行:

    >>> subprocess.run("exit 1", shell=True, check=True)
    Traceback (most recent call last):
      ...
    subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
    

    捕获输出:

    >>> subprocess.run(["ls", "-l", "/dev/null"], stdout=subprocess.PIPE)
    CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
    stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n')
    

    原始答案:

    我建议尝试Envoy . 它是子进程的包装器,而后者又是aims to replace旧的模块和函数 . 特使是人类的子过程 .

    the readme的用法示例:

    >>> r = envoy.run('git config', data='data to pipe in', timeout=2)
    
    >>> r.status_code
    129
    >>> r.std_out
    'usage: git config [options]'
    >>> r.std_err
    ''
    

    管道周围的东西:

    >>> r = envoy.run('uptime | pbcopy')
    
    >>> r.command
    'pbcopy'
    >>> r.status_code
    0
    
    >>> r.history
    [<Response 'uptime'>]
    
  • 95

    我建议使用子进程模块而不是os.system,因为它会为你进行shell转义,因此更加安全:http://docs.python.org/library/subprocess.html

    subprocess.call(['ping', 'localhost'])
    
  • 57

    以下是调用外部程序的方法及其优缺点的摘要:

    • os.system("some_command with args") 将命令和参数传递给系统的shell . 这很好,因为您实际上可以以这种方式一次运行多个命令并设置管道和输入/输出重定向 . 例如:
    os.system("some_command < input_file | another_command > output_file")
    

    但是,虽然这很方便,但您必须手动处理shell字符的转义,例如空格等 . 另一方面,这也允许您运行只是shell命令而不是外部程序的命令 . 见the documentation .

    • stream = os.popen("some_command with args") 将执行与_77120相同的操作,除了它为您提供了一个类似文件的对象,您可以使用该对象访问该进程的标准输入/输出 . 还有3种其他的popen变体,它们对i / o的处理方式略有不同 . 如果您将所有内容都作为字符串传递,那么您的命令将传递给shell;如果你将它们作为列表传递,那么你不必担心逃避任何事情 . 见the documentation .

    • subprocess 模块的 Popen 类 . 这是为了取代 os.popen ,但由于其如此全面,因此具有稍微复杂的缺点 . 例如,你会说:

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

    代替:

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

    但是在一个统一的类而不是4个不同的popen函数中拥有所有选项是很好的 . 见the documentation .

    • subprocess 模块中的 call 函数 . 这基本上就像 Popen 类一样,并且接受所有相同的参数,但它只是等待命令完成并为您提供返回代码 . 例如:
    return_code = subprocess.call("echo Hello World", shell=True)
    

    the documentation .

    • 如果您使用的是Python 3.5或更高版本,则可以使用新的subprocess.run函数,该函数与上述函数非常相似,但更灵活,并在命令完成执行时返回CompletedProcess对象 .

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

    subprocess 模块可能应该是你使用的 .

    最后请注意,对于所有将shell执行的最终命令作为字符串传递的方法,您负责转义它 . There are serious security implications 如果您传递的字符串的任何部分无法完全信任 . 例如,如果用户正在输入字符串的某些/任何部分 . 如果您不确定,请仅将这些方法与常量一起使用 . 为了给您一些暗示,请考虑以下代码:

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

    并想象用户输入“我的妈妈不爱我&& rm -rf /” .

  • 57

    使用:

    import os
    
    cmd = 'ls -al'
    
    os.system(cmd)
    

    os - This module provides a portable way of using operating system-dependent functionality.

    对于更多 os 函数,here是文档 .

  • 50

    检查“pexpect”Python库 .

    它允许交互式控制外部程序/命令,甚至ssh,ftp,telnet等 . 您只需键入以下内容:

    child = pexpect.spawn('ftp 192.168.0.24')
    
    child.expect('(?i)name .*: ')
    
    child.sendline('anonymous')
    
    child.expect('(?i)password')
    
  • 33

    在Linux下,如果你想调用一个独立执行的外部命令(将在python脚本终止后继续运行),你可以使用一个简单的队列作为task spoolerat命令

    任务假脱机程序的示例:

    import os
    os.system('ts <your-command>')
    

    有关任务假脱机程序的注释( ts ):

    • 您可以使用以下命令设置要运行的并发进程数(“slots”):

    ts -S <number-of-slots>

    • 安装 ts 不需要管理员权限 . 您可以使用简单的 make 从源代码下载并编译它,将其添加到您的路径中,您就完成了 .
  • 23

    os.system 没关系,但有点过时了 . 它也不是很安全 . 相反,请尝试 subprocess . subprocess 不直接调用sh,因此比 os.system 更安全 .

    获取更多信息here .

  • 17

    我总是使用 fabric 这样的东西:

    from fabric.operations import local
    result = local('ls', capture=True)
    print "Content:/n%s" % (result, )
    

    但这似乎是一个很好的工具:sh (Python subprocess interface) .

    看一个例子:

    from sh import vgdisplay
    print vgdisplay()
    print vgdisplay('-v')
    print vgdisplay(v=True)
    
  • 13

    无耻的插件,我为此写了一个库:P https://github.com/houqp/shell.py

    它现在基本上是popen和shlex的包装器 . 它还支持管道命令,因此您可以在Python中更轻松地链接命令 . 所以你可以这样做:

    ex('echo hello shell.py') | "awk '{print $2}'"
    
  • 12

    如果您不想测试返回值,则 subprocess.check_call 很方便 . 它会对任何错误抛出异常 .

  • 23

    有许多不同的库允许您使用Python调用外部命令 . 对于每个库,我已经给出了描述并显示了调用外部命令的示例 . 我用作示例的命令是 ls -l (列出所有文件) . 如果您想了解有关我列出的任何库的更多信息,并链接每个库的文档 .

    资料来源:

    这些都是图书馆:

    希望这可以帮助您决定使用哪个库:)

    子进程

    子进程允许您调用外部命令并将它们连接到它们的输入/输出/错误管道(stdin,stdout和stderr) . 子进程是运行命令的默认选择,但有时其他模块更好 .

    subprocess.run(["ls", "-l"]) # Run command
    subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output
    subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command
    

    os

    os用于"operating system dependent functionality" . 它还可以用于使用 os.systemos.popen 调用外部命令(注意:还有一个subprocess.popen) . 操作系统将始终运行shell,对于那些不知道如何使用 subprocess.run 的人来说,它是一个简单的选择 .

    os.system("ls -l") # run command
    os.popen("ls -l").read() # This will run the command and return any output
    

    sh

    sh是一个子进程接口,它允许您像调用函数一样调用程序 . 如果要多次运行命令,这非常有用 .

    sh.ls("-l") # Run command normally
    ls_cmd = sh.Command("ls") # Save command as a variable
    ls_cmd() # Run command as if it were a function
    

    plumbum是"script-like" Python程序的库 . 您可以像 sh 中那样调用类似函数的程序 . 如果要运行没有shell的管道,Plumbum很有用 .

    ls_cmd = plumbum.local("ls -l") # get command
    ls_cmd() # run command
    

    pexpect

    pexpect允许您生成子应用程序,控制它们并在其输出中查找模式 . 对于期望在Unix上使用tty的命令,这是替代子进程的更好的替代方法 .

    pexpect.run("ls -l") # Run command as normal
    child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
    child.expect('Password:') # When this is the output
    child.sendline('mypassword')
    

    面料

    fabric是一个Python 2.5和2.7库 . 它允许您执行本地和远程shell命令 . Fabric是在安全shell(SSH)中运行命令的简单替代方法

    fabric.operations.local('ls -l') # Run command as normal
    fabric.operations.local('ls -l', capture = True) # Run command and receive output
    

    特使

    特使被称为"subprocess for humans" . 它被用作 subprocess 模块周围的便利包装器 .

    r = envoy.run("ls -l") # Run command
    r.std_out # get output
    

    命令

    commands 包含 os.popen 的包装函数,但它已从Python 3中删除,因为 subprocess 是更好的选择 .

    该编辑基于J.F. Sebastian的评论 .

  • 176

    这就是我运行命令的方式 . 此代码包含您需要的所有内容

    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()
    
  • 3745

    查看标准库中的subprocess module

    from subprocess import call
    call(["ls", "-l"])
    

    subprocesssystem 的优点是它更灵活(你可以获得stdout,stderr,"real"状态代码,更好的错误处理等等) .

    official documentation推荐使用 subprocess 模块替代os.system():

    子流程模块提供了更强大的工具来生成新流程并检索其结果;使用该模块比使用此函数[os.system()]更可取 .

    subprocess 文档中的“Replacing Older Functions with the subprocess Module”部分可能有一些有用的配方 .

    有关 subprocess 模块的官方文档:

  • 14

    我通常使用:

    import subprocess
    
    p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    for line in p.stdout.readlines():
        print line,
    retval = p.wait()
    

    您可以使用管道中的 stdout 数据自由地执行所需操作 . 实际上,您可以简单地省略这些参数( stdout=stderr= ),它的行为类似于 os.system() .

  • 27

    还有Plumbum

    >>> from plumbum import local
    >>> ls = local["ls"]
    >>> ls
    LocalCommand(<LocalPath /bin/ls>)
    >>> ls()
    u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
    >>> notepad = local["c:\\windows\\notepad.exe"]
    >>> notepad()                                   # Notepad window pops up
    u''                                             # Notepad window is closed by user, command returns
    

相关问题