shell subprocess popen返回值 - 在Python中调用外部命令




15 Answers

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

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

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

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

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

  3. 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函数中拥有所有选项是很好的。 请参阅文档

  4. subprocess模块的call函数。 这基本上就像Popen类一样,并且接受所有相同的参数,但它只是等待命令完成并为您提供返回代码。 例如:

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

    请参阅文档

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

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

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

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

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

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

os system参数 python获取shell执行结果

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




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

假设您想从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 流程创建标志传递给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中启动后台进程尚未解决任何问题。




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

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



我总是使用fabric这样的事情:

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

但这似乎是一个很好的工具: sh (Python子进程接口)

看一个例子:

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



有许多不同的库允许您使用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.systemos.popen调用外部命令(注意:还有一个os.popen )。 os将始终运行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是一个用于“类似脚本”的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模块周围的便利包装器。

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

命令

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

该编辑基于JF Sebastian的评论。




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

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



更新:

如果您的代码不需要保持与早期Python版本的兼容性,则从Python 3.5开始 ,推荐使用subprocess.run。 它更加一致,并提供与Envoy类似的易用性。 (管道不是那么简单。看看这个问题是怎么回事 。)

以下是文档中的一些示例。

运行一个过程:

>>> 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')

原始答案:

我建议尝试https://github.com/kennethreitz/envoy 。 它是子进程的包装器,后者又旨在替换旧的模块和功能。 特使是人类的子过程。

自述文件中的示例用法:

>>> 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'>]






还有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



它可以很简单:

import os
cmd = "your command"
os.system(cmd)



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




os.system不允许您存储结果,因此如果您想将结果存储在某些列表或os.system中。




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

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

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



我非常喜欢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



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

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

输出:

27a74fcd-37c0-4789-9414-9531b7e3f126



Related