首页 / 资讯中心 / 文章详情

KBEngine 内置 Python 的 subprocess 模块完全指南:进程管理、参数详解与实战

KBEngine 内置 Python 的 subprocess 模块完全指南:进程管理、参数详解与实战 ★ FEATURED ARTICLE
游戏开发后端【免费下载链接】kbengineA MMOG engine of server.项目地址https://gitcode.com/gh_mirrors/kb/kbengine点击查看免费下载本指南以 KBEngine 仓库内置 Python 3.7.3见 patchlevel.h随附的标准库文档 subprocess.rst 为骨架系统讲解subprocess模块的进程派生、管道通信、超时控制与异常体系。读完本文你将掌握run()与Popen两代 API 的全部常用参数与调用模式、call/check_call/check_output等新旧函数的等价关系、shellTrue的安全边界以及如何把该模块用于 KBEngine 服务器安装脚本这类真实运维场景。一、subprocess 是什么新一代子进程管理接口subprocess模块允许你在 Python 中派生新进程、连接其标准输入/输出/错误管道并获取其返回码。它的设计意图是取代一批旧模块与旧函数主要包括os.system os.spawn*也就是说凡是过去靠os.system执行命令、靠os.spawn*家族派生进程的写法都应优先迁移到subprocess。文档中提供了完整的“替换对照表”见后文第五节并指出所有被替换的旧函数在“执行程序找不到”时会多多少少静默失败而subprocess的替代写法会抛出OSError行为更明确、更安全。在 KBEngine 仓库中该模块被内置 Python 标准库收录源码实现位于 Lib/subprocess.pyrun实现于第 431 行、Popen类实现于第 608 行、CompletedProcess实现于第 398 行并有完整的单元测试 test/test_subprocess.py 佐证其行为。仓库工具脚本中也大量使用它例如 tools/server/install/installer.py 中便通过subprocess.Popen执行系统命令并采集输出。二、推荐入口run() 函数与 CompletedProcess2.1 run() 函数签名run()是 Python 3.5 起推荐的统一入口适用于它能覆盖的所有场景更高级的场景再退回底层Popen接口。其签名简化版为subprocess.run(args, *, stdinNone, inputNone, stdoutNone, stderrNone, capture_outputFalse, shellFalse, cwdNone, timeoutNone, checkFalse, encodingNone, errorsNone, textNone, envNone, universal_newlinesNone)执行由args描述的命令等待命令结束后返回一个CompletedProcess实例。上面列出的是最常见参数因此采用仅限关键字的形式完整签名与Popen构造函数基本一致绝大多数参数会直接透传给Popen。其中timeout、input、check、capture_output四个参数不会透传。若capture_output为真则 stdout 与 stderr 都会被捕获内部会自动以stdoutPIPE、stderrPIPE创建Popen对象此时不能再同时指定stdout或stderr参数。timeout参数透传给Popen.communicate若超时子进程会被杀死并等待其结束随后重新抛出TimeoutExpired异常。input参数透传给Popen.communicate作为子进程的 stdin 数据必须是字节序列或在指定了encoding/errors、或textTrue时可以是字符串。使用它时内部会自动以stdinPIPE创建Popen不能再同时指定stdin参数。checkTrue时若进程以非零退出码结束将抛出CalledProcessError该异常的属性中保存着参数、退出码以及被捕获的 stdout/stderr。指定encoding/errors或textTrue时stdin/stdout/stderr 文件对象会以文本模式打开使用指定的编码与错误处理方式或io.TextIOWrapper的默认值universal_newlines与text等价仅为向后兼容保留。默认情况下文件对象以二进制模式打开。env非None时必须是定义新进程环境变量的映射用于替代默认的“继承当前进程环境”行为直接透传给Popen。2.2 运行示例文档给出的三个经典示例 subprocess.run([ls, -l]) # 不捕获输出 CompletedProcess(args[ls, -l], returncode0) subprocess.run(exit 1, shellTrue, checkTrue) Traceback (most recent call last): ... subprocess.CalledProcessError: Command exit 1 returned non-zero exit status 1 subprocess.run([ls, -l, /dev/null], capture_outputTrue) CompletedProcess(args[ls, -l, /dev/null], returncode0, stdoutbcrw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n, stderrb)注意第三个示例中stdout是bytes序列因为未开启文本模式若传入encoding/errors或textTrue则变为字符串。2.3 CompletedProcess 类CompletedProcess是run()的返回值表示一个已结束的进程包含以下属性属性含义args启动进程所用的参数可能是列表或字符串returncode子进程退出状态通常 0 表示成功负值-N表示被信号N终止仅 POSIXstdout捕获到的子进程 stdout。字节序列或在调用时指定了encoding/errors/textTrue时为字符串未捕获则为None。若以stderrsubprocess.STDOUT运行stdout 与 stderr 会合并到该属性stderr属性为Nonestderr捕获到的子进程 stderr规则同stdout此外还有方法check_returncode()若returncode非零则抛出CalledProcessError这是手动复查退出状态的便捷手段。2.4 三个特殊常量DEVNULL用作stdin/stdout/stderr参数表示使用特殊文件os.devnull丢弃/不读取数据。PIPE表示打开通向标准流的管道最常配合Popen.communicate使用。STDOUT仅用于stderr参数表示标准错误与标准输出共用同一句柄合并输出。三、常用参数深度解析Frequently Used ArgumentsPopen构造函数以及各便捷函数支持大量可选参数以覆盖多种使用场景但大多数场景用默认值即可。最常被修改的参数如下。3.1 args参数序列优先于字符串args是所有调用都必需的参数可以是字符串或程序参数序列。官方推荐优先传序列因为模块会替你处理转义与引号例如文件名含空格时。若传单个字符串则要么shellTrue见下文要么该字符串只能是无参数的纯程序名。3.2 stdin / stdout / stderr标准流重定向三个参数分别指定被执行程序的标准输入、标准输出、标准错误句柄。合法取值PIPE为子进程新建一条管道DEVNULL使用os.devnull一个已存在的文件描述符正整数一个已存在的文件对象None默认不做任何重定向子进程句柄直接继承自父进程。此外stderr还可以取STDOUT表示把子进程的 stderr 数据捕获进与 stdout 相同的句柄。3.3 文本模式text / encoding / errors / universal_newlines若指定了encoding或errors或text即universal_newlines为真则 stdin/stdout/stderr 文件对象会以文本模式打开使用调用中指定的encoding/errors或io.TextIOWrapper的默认值。文本模式下对stdin输入中的换行符\n会被转换为默认行分隔符os.linesep对stdout/stderr输出中所有行尾都会被转换为\n详见io.TextIOWrapper在newlineNone时的文档。如果不使用文本模式三个流均以二进制流打开不做任何编码或换行转换。自 3.3 起universal_newlinesTrue时使用locale.getpreferredencoding(False)而非locale.getpreferredencoding()。注意Popen.communicate方法不会更新Popen.stdin、Popen.stdout、Popen.stderr文件对象的newlines属性。3.4 shellTrue何时需要 shell若shell为True指定命令将通过 shell 执行。当你主要用 Python 的增强控制流、但仍想利用 shell 的管道、文件名通配符、环境变量展开以及~展开到用户主目录等特性时这会很方便。不过 Python 自身其实已提供大量类似功能glob、fnmatch、os.walk、os.path.expandvars、os.path.expanduser、shutil。安全提示使用shellTrue之前务必先阅读“安全考虑”一节。3.5 cwd 与 envcwd若非None执行子进程前会切换到该工作目录cwd可以是str或 path-like 对象3.6 起支持若可执行文件路径是相对路径则会相对cwd查找executable或args首项。env若非None则使用该映射作为新进程的环境变量替代默认的继承行为。注意若指定了env必须提供程序运行所需的全部变量——在 Windows 上为了运行 side-by-side assemblyenv必须包含有效的SystemRoot。四、异常体系与安全考虑4.1 异常类型SubprocessError本模块所有其他异常的基类3.3 起。TimeoutExpiredSubprocessError的子类等待子进程超时时抛出。属性包括cmd派生子进程所用的命令、timeout秒数、output若被run/check_output捕获则为子进程输出否则None、stdoutoutput的别名与stderr对称、stderr若被run捕获则为 stderr 输出否则None。CalledProcessErrorSubprocessError的子类check_call或check_output遇到非零退出状态时抛出。属性包括returncode若因信号退出则为负数信号号、cmd、output、stdout别名、stderr。除了模块自定义异常还有两类常见情况在子进程真正开始执行新程序前抛出的异常会在父进程中重新抛出最常见的是OSError例如要执行的文件不存在应用应做好捕获准备。传入非法参数给Popen会抛出ValueError。所有接受timeout参数的函数与方法如call、Popen.communicate在超时前未等到进程退出时都会抛出TimeoutExpired。4.2 安全考虑shell 注入防护与某些popen函数不同本实现绝不会隐式调用系统 shell。这意味着所有字符包括 shell 元字符都可以安全地传给子进程。但若显式通过shellTrue调用 shell则应用自身有责任对所有空白与元字符做恰当的引号转义以避免 shell 注入漏洞。构造 shell 命令字符串时应使用shlex.quote函数对字符串做正确转义。五、底层接口Popen 构造函数与对象Popen类负责模块底层的一切进程创建与管理提供极大的灵活性用于处理便捷函数覆盖不到的少见场景。在 POSIX 上它采用os.execvp类似行为执行子程序在 Windows 上采用CreateProcess()。5.1 构造函数完整签名subprocess.Popen(args, bufsize-1, executableNone, stdinNone, stdoutNone, stderrNone, preexec_fnNone, close_fdsTrue, shellFalse, cwdNone, envNone, universal_newlinesNone, startupinfoNone, creationflags0, restore_signalsTrue, start_new_sessionFalse, pass_fds(), *, encodingNone, errorsNone, textNone)各参数要点args程序参数序列或单个字符串。默认执行args首项序列形式。若为字符串解释方式依平台而定POSIX 上将字符串解释为要执行的程序名或路径仅限不带参数时Windows 上序列会被按“参数序列转字符串”规则见第七节转换。shell默认False。True时推荐以字符串形式传args。POSIX 上默认 shell 为/bin/shargs为字符串时它就是要通过 shell 执行的命令须与在 shell 提示符下输入时完全一致包括空格文件名要加引号或反斜杠转义args为序列时等价于Popen([/bin/sh, -c, args[0], args[1], ...])。Windows 上默认 shell 由环境变量COMSPEC指定仅当命令是 shell 内建命令如dir、copy或运行批处理文件/控制台程序时才需要shellTrue。bufsize创建 stdin/stdout/stderr 管道文件对象时传给open的对应参数0为无缓冲1为行缓冲仅文本模式下可用其他正值表示约该大小的缓冲区负数默认使用系统默认io.DEFAULT_BUFFER_SIZE。executable很少用到。shellFalse时它替换args指定的程序但原始args仍传给程序POSIX 上args名会成为ps等工具中的显示名。shellTrue时POSIX它指定替换默认/bin/sh的 shell。preexec_fn若设为可调用对象会在子进程执行前调用仅 POSIX。警告在线程环境中使用preexec_fn不安全子进程可能在 exec 前死锁如必须用务必保持简单并尽量减少调用的库。需要修改子进程环境时请用env参数而不是在preexec_fn里做start_new_session可替代过去用preexec_fn调用os.setsid()的常见用法。close_fds为真时除0、1、2外的所有文件描述符都会在子进程执行前关闭为假时文件描述符遵循其 inheritable 标志。Windows 上为真时不继承任何句柄除非在STARTUPINFO.lpAttributeList的handle_list中显式传入或通过标准句柄重定向。3.7 起 Windows 上重定向标准句柄时默认值也变为True。pass_fds可选的、需要在父子进程间保持打开的文件描述符序列提供任何pass_fds都会强制close_fdsTrue仅 POSIX。restore_signals为真默认时Python 已设为 SIG_IGN 的所有信号当前包括 SIGPIPE、SIGXFZ、SIGXFSZ在 exec 前于子进程中恢复为 SIG_DFL仅 POSIX。start_new_session为真时执行子进程前会调用setsid()系统调用仅 POSIX。startupinfo / creationflagsWindows 专用见第七节。5.2 Popen 支持上下文管理器Popen对象可作为上下文管理器使用3.2 起退出with块时关闭标准文件描述符并等待进程结束with Popen([ifconfig], stdoutPIPE) as proc: log.write(proc.stdout.read())另外3.6 起若子进程仍在运行Popen析构函数会发出ResourceWarning警告提醒及时回收。5.3 Popen 对象方法poll()检查子进程是否已终止设置并返回returncode属性未终止则返回None。wait(timeoutNone)等待子进程终止设置并返回returncode。超过timeout秒未终止则抛TimeoutExpired捕获后可以安全地重试等待。注意使用stdoutPIPE或stderrPIPE且子进程输出足以撑满 OS 管道缓冲区时wait()会死锁子进程阻塞等待管道被读取——使用管道时请用communicate()。该函数内部用忙循环非阻塞调用加短眠实现需要异步等待时可用asyncio模块的asyncio.create_subprocess_exec。communicate(inputNone, timeoutNone)与进程交互——向 stdin 发送数据、读取 stdout/stderr 直到 EOF、等待进程终止返回(stdout_data, stderr_data)元组。文本模式下input须为字符串否则为 bytes。要发送 stdin 数据须以stdinPIPE创建要拿到非None的输出须同时给stdoutPIPE和/或stderrPIPE。若超时抛TimeoutExpired捕获后重试通信不会丢失任何输出。超时并不会自动杀死子进程规范做法是主动 kill 后补一次通信proc subprocess.Popen(...) try: outs, errs proc.communicate(timeout15) except TimeoutExpired: proc.kill() outs, errs proc.communicate()注意communicate读取的数据在内存中缓冲数据量大或无限时不要使用该方法。send_signal(signal)向子进程发送信号。Windows 上 SIGTERM 是terminate的别名CTRL_C_EVENT 与 CTRL_BREAK_EVENT 可发送给以包含CREATE_NEW_PROCESS_GROUP的creationflags启动的进程。terminate()停止子进程。POSIX 发 SIGTERMWindows 调用 Win32 APITerminateProcess。kill()杀死子进程。POSIX 发 SIGKILLWindows 上kill是terminate的别名。5.4 Popen 对象属性属性含义args传给Popen的args程序参数序列或单个字符串stdin若stdinPIPE为可写流对象文本或字节流取决于encoding/errors/universal_newlines否则为Nonestdout若stdoutPIPE为可读流对象读取即得到子进程输出否则为Nonestderr若stderrPIPE为可读流对象否则为Nonepid子进程进程 ID。注意若shellTrue这是被派生的 shell 的 PIDreturncode由poll/wait/communicate设置None表示进程尚未终止负值-N表示被信号N终止仅 POSIX死锁警告应使用communicate()而非Popen.stdin.write、Popen.stdout.read、Popen.stderr.read以避免任一 OS 管道缓冲区被填满而阻塞子进程导致的死锁。六、旧版高级 APIcall / check_call / check_outputPython 3.5 之前这三个函数构成高级 APIrun()出现后新代码应优先用run()但存量代码大量仍在调用它们。call(args, *, stdinNone, stdoutNone, stderrNone, shellFalse, cwdNone, timeoutNone)运行命令并等待结束返回Popen.returncode。等价于run(...).returncode但不支持input与check参数。不要与stdoutPIPE或stderrPIPE连用——管道没人读取子进程输出撑满 OS 管道缓冲区时会阻塞。check_call(...)同call但返回码非零时抛CalledProcessError其returncode属性保存返回码。等价于run(..., checkTrue)不支持input。同样不建议与PIPE连用。check_output(args, *, stdinNone, stderrNone, shellFalse, cwdNone, encodingNone, errorsNone, universal_newlinesNone, timeoutNone, textNone)运行命令并返回其输出。返回码非零抛CalledProcessError输出保存在其output属性。等价于run(..., checkTrue, stdoutPIPE).stdout。默认返回编码后的 bytes实际编码取决于被调用的命令解码常需在应用层处理设universal_newlinesTrue可覆盖。不支持显式传inputNone来继承父进程标准输入。合并捕获 stderr 用stderrsubprocess.STDOUT subprocess.check_output( ... ls non_existent_file; exit 0, ... stderrsubprocess.STDOUT, ... shellTrue) ls: non_existent_file: No such file or directory\n七、替换旧函数对照表“a 变成 b”表示 b 可作为 a 的替代。所有被替换的 “a” 函数在程序找不到时都会多或少静默失败而替代写法抛OSError使用check_output的替代写法在非零返回码时会抛CalledProcessError输出仍可从其output属性获取。替换 shell 反引号outputmycmd myargoutput check_output([mycmd, myarg])替换 shell 管道outputdmesg | grep hdap1 Popen([dmesg], stdoutPIPE) p2 Popen([grep, hda], stdinp1.stdout, stdoutPIPE) p1.stdout.close() # 允许 p1 在 p2 退出时收到 SIGPIPE output p2.communicate()[0]p1.stdout.close()这一步很重要若 p2 先于 p1 退出p1 需要收到 SIGPIPE。对可信输入也可直接用 shell 管道output check_output(dmesg | grep hda, shellTrue)替换 os.systemsts os.system(mycmd myarg) # 变成 sts call(mycmd myarg, shellTrue)更贴近现实的完整写法含信号与 OSError 处理try: retcode call(mycmd myarg, shellTrue) if retcode 0: print(Child was terminated by signal, -retcode, filesys.stderr) else: print(Child returned, retcode, filesys.stderr) except OSError as e: print(Execution failed:, e, filesys.stderr)替换 os.spawn 家族pid os.spawnlp(os.P_NOWAIT, /bin/mycmd, mycmd, myarg) # pid Popen([/bin/mycmd, myarg]).pid retcode os.spawnlp(os.P_WAIT, /bin/mycmd, mycmd, myarg) # retcode call([/bin/mycmd, myarg]) os.spawnvp(os.P_NOWAIT, path, args) # Popen([path] args[1:]) os.spawnlpe(os.P_NOWAIT, /bin/mycmd, mycmd, myarg, env) # Popen([/bin/mycmd, myarg], env{PATH: /usr/bin})替换 os.popen / os.popen2 / os.popen3 / os.popen4(child_stdin, child_stdout) os.popen2(cmd, mode, bufsize) # p Popen(cmd, shellTrue, bufsizebufsize, stdinPIPE, stdoutPIPE, close_fdsTrue) (child_stdin, child_stdout) (p.stdin, p.stdout) (child_stdin, child_stdout, child_stderr) os.popen3(cmd, mode, bufsize) # p Popen(cmd, shellTrue, bufsizebufsize, stdinPIPE, stdoutPIPE, stderrPIPE, close_fdsTrue) (child_stdin, child_stdout, child_stderr) (p.stdin, p.stdout, p.stderr) (child_stdin, child_stdout_and_stderr) os.popen4(cmd, mode, bufsize) # p Popen(cmd, shellTrue, bufsizebufsize, stdinPIPE, stdoutPIPE, stderrSTDOUT, close_fdsTrue) (child_stdin, child_stdout_and_stderr) (p.stdin, p.stdout)返回码处理对照pipe os.popen(cmd, w) ... rc pipe.close() if rc is not None and rc 8: print(There were some errors) # process Popen(cmd, stdinPIPE) ... process.stdin.close() if process.wait() ! 0: print(There were some errors)替换 popen2 模块若cmd是字符串命令通过/bin/sh执行若是列表则直接执行(child_stdout, child_stdin) popen2.popen2(somestring, bufsize, mode) # p Popen(somestring, shellTrue, bufsizebufsize, stdinPIPE, stdoutPIPE, close_fdsTrue) (child_stdout, child_stdin) (p.stdout, p.stdin) (child_stdout, child_stdin) popen2.popen2([mycmd, myarg], bufsize, mode) # p Popen([mycmd, myarg], bufsizebufsize, stdinPIPE, stdoutPIPE, close_fdsTrue) (child_stdout, child_stdin) (p.stdout, p.stdin)popen2.Popen3/Popen4基本等同subprocess.Popen区别在于Popen执行失败会抛异常capturestderr参数被stderr参数取代必须显式指定stdinPIPE与stdoutPIPEpopen2 默认关闭所有文件描述符而用Popen需显式close_fdsTrue才能在所有平台/旧版 Python 上保证此行为。八、遗留 shell 调用函数getstatusoutput / getoutput这两个函数来自 2.x 的commands模块会隐式调用系统 shell前文所述的安全性与异常处理一致性保证对它们不适用仅作兼容保留。getstatusoutput(cmd)在 shell 中执行字符串cmd返回(exitcode, output)二元组通过Popen.check_output实现使用 locale 编码。输出末尾的换行会被去除。示例 subprocess.getstatusoutput(ls /bin/ls) (0, /bin/ls) subprocess.getstatusoutput(cat /bin/junk) (1, cat: /bin/junk: No such file or directory) subprocess.getstatusoutput(/bin/junk) (127, sh: /bin/junk: not found) subprocess.getstatusoutput(/bin/kill $$) (-15, )getoutput(cmd)与getstatusoutput类似但忽略退出码仅返回包含命令输出stdout 与 stderr的字符串 subprocess.getoutput(ls /bin/ls) /bin/ls两者 POSIX 与 Windows 均可用。九、Windows 专属能力9.1 STARTUPINFO 与进程创建STARTUPINFO类与下列常量仅 Windows 可用用于精细控制进程创建部分支持 WindowsSTARTUPINFO结构STARTUPINFO(*, dwFlags0, hStdInputNone, hStdOutputNone, hStdErrorNone, wShowWindow0, lpAttributeListNone)dwFlags决定某些STARTUPINFO属性在进程创建窗口时是否生效的位字段常用组合如STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW。hStdInput/hStdOutput/hStdError当dwFlags指定STARTF_USESTDHANDLES时分别指定进程的标准输入/输出/错误句柄否则各自使用默认键盘缓冲区/控制台窗口缓冲区。wShowWindow当dwFlags指定STARTF_USESHOWWINDOW时可为ShowWindow函数nCmdShow参数允许的任何值SW_SHOWDEFAULT除外SW_HIDE用于此属性Popen以shellTrue调用时即使用它。lpAttributeListSTARTUPINFOEX的附加属性字典支持handle_list将被继承的句柄序列非空时close_fds必须为真。传入Popen构造函数前这些句柄须先用os.set_handle_inheritable临时设为可继承否则会抛 Windows 错误ERROR_INVALID_PARAMETER(87)。9.2 creationflags 常量Popen的creationflags参数可选以下常量可组合常量含义CREATE_NEW_CONSOLE新进程使用新控制台而非继承父控制台CREATE_NEW_PROCESS_GROUP创建新进程组用os.kill杀子进程所必需与CREATE_NEW_CONSOLE同时指定时被忽略ABOVE_NORMAL_PRIORITY_CLASS/BELOW_NORMAL_PRIORITY_CLASS高于/低于平均优先级HIGH_PRIORITY_CLASS/IDLE_PRIORITY_CLASS/NORMAL_PRIORITY_CLASS高/空闲最低/正常默认优先级REALTIME_PRIORITY_CLASS实时优先级几乎不该用会打断管理鼠标、键盘输入与后台磁盘刷新的系统线程CREATE_NO_WINDOW新进程不创建窗口DETACHED_PROCESS新进程不继承父控制台不能与CREATE_NEW_CONSOLE同用CREATE_DEFAULT_ERROR_MODE新进程不继承调用进程的错误模式而使用默认错误模式对以禁用硬错误运行的多线程 shell 应用尤其有用CREATE_BREAKAWAY_FROM_JOB新进程不与作业关联另有STD_INPUT_HANDLE/STD_OUTPUT_HANDLE/STD_ERROR_HANDLE标准输入/输出/错误设备初始为控制台缓冲区CONIN$/CONOUT$与SW_HIDE隐藏窗口等常量。9.3 Windows 上参数序列转字符串规则Windows 上args序列会被转换为可被 MS C 运行时解析的字符串规则如下参数以空白空格或制表符分隔被双引号包围的字符串视为单个参数不论其内部空白带引号字符串可嵌入参数中反斜杠前加双引号该双引号视为字面双引号反斜杠按字面解释除非它紧跟在双引号前若反斜杠紧跟在双引号前每对反斜杠解释为一个字面反斜杠若反斜杠数量为奇数最后一个反斜杠按规则 3 转义其后的双引号。相关解析与转义工具可参考shlex模块。十、仓库实战KBEngine 中的 subprocess 使用模式KBEngine 仓库内置的正是本指南所对应的 Python 3.7.3见 patchlevel.h 第 26 行因此本文档所述的run()/capture_output/text等 3.53.7 特性在该环境中均可直接使用实现与测试可在 Lib/subprocess.py 与 test/test_subprocess.py 中查看。在服务器运维侧tools/server/install/installer.py 提供了贴近实际的调用样本import subprocess from subprocess import check_call ... cret subprocess.Popen(cmdstr, shellTrue, stdoutsubprocess.PIPE, stderrsubprocess.STDOUT).stdout.readlines() ... ret subprocess.Popen(whereis mysql, shellTrue, stdoutsubprocess.PIPE, stderrsubprocess.STDOUT).stdout.readlines()[0].split()可以看出该脚本采用了shellTruestdoutPIPEstderrSTDOUT的组合让命令经 shell 执行便于拼接重定向与多个命令、把标准错误合并进标准输出、随后读取全部行。这正是“捕获命令输出、判断命令是否可用”如探测 mysql 安装位置这类运维场景的常见写法。参考本文第四节的建议当这类命令串中包含外部可控输入时应改用参数序列或shlex.quote规避注入风险需要同步等待并校验退出码时则可升级为run(..., checkTrue, capture_outputTrue, textTrue)的现代写法。小结subprocess是 Python 中创建与管理子进程的标准答案日常场景用run()一把梭需要流式交互或精细控制时下沉到Popencall/check_call/check_output用于兼容存量代码getstatusoutput/getoutput仅作遗留兼容。核心要点可归纳为四句话能用参数序列就别用 shell 字符串、用管道必配communicate防死锁、涉及外部输入必须防注入、需要校验结果就用checkTrue并捕获CalledProcessError。在 KBEngine 内置的 Python 3.7.3 环境中上述全部 API 均可直接使用仓库的安装脚本 installer.py 即是现成的实战参照。赞分享游戏开发后端【免费下载链接】kbengineA MMOG engine of server.项目地址https://gitcode.com/gh_mirrors/kb/kbengine点击查看免费下载相关推荐KBEngine 内置 Python 3.7 多进程编程实战multiprocessing 模块完全指南与源码级解析KBEngine 内置 Python 3.7 多进程编程实战multiprocessing 模块完全指南与源码级解析 KBEngine gh_mirrors游戏开发后端KBEngine 内置 Python 3.7.3 数据类详解dataclasses 模块从原理到实战KBEngine 内置 Python 3.7.3 数据类详解dataclasses 模块从原理到实战 KBEngine 服务端脚本引擎基于 Python 实现游戏开发后端KBEngine 内置 Python 的 html 模块完全指南escape、unescape 与 HTMLParser 实战解析KBEngine 内置 Python 的 html 模块完全指南escape、unescape 与 HTMLParser 实战解析 本文以 KBEngine游戏开发后端上一篇【亲测免费】 圆形图(circularGraph)项目教程下一篇探索废弃之美重拾Twig Extensions的遗珠之光创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?
咨询建站