asyncio.create_subprocess_exec()不能传字符串命令,因其直连execve系统调用、不经过shell解析,必须传程序名与分离参数列表(如["ls", "-l"]),否则因查找不存在的完整字符串文件而报FileNotFoundError。

asyncio.create_subprocess_exec() 传字符串命令直接报 FileNotFoundError
它不走 shell,而是直连 execve 系统调用,所以必须拆成程序名 + 参数列表。传 "ping -c 1 google.com" 就等于找一个叫这个完整字符串的可执行文件,当然不存在。
常见错误写法:await asyncio.create_subprocess_exec("ls -l /tmp") → 报 FileNotFoundError
- 正确写法是手动拆解:
["ls", "-l", "/tmp"] - 路径含空格时(尤其 Windows),别自己拼字符串,直接把路径作为独立列表项传入,如
["C:\Program Files\nodejs\node.exe", "--version"],系统会自动处理 - 真需要通配符、管道、环境变量展开等 shell 功能,就换用
create_subprocess_shell(),别硬塞字符串进exec
subprocess.run() 默认不捕获输出,容易卡死或丢失结果
默认 stdout=None 表示继承父进程 stdout(通常是终端),在后台异步任务里会导致输出不可控,甚至子进程因管道满而挂起。
安全捕获的关键参数组合:
立即学习“Python免费学习笔记(深入)”;
-
capture_output=True:等价于stdout=PIPE, stderr=PIPE -
text=True:返回字符串而非 bytes,避免后续手动.decode() -
check=True:非零退出码直接抛CalledProcessError,不靠 returncode 判断 -
timeout=30:防止命令无限 hang,尤其涉及网络或用户输入时
错误示范:subprocess.run(["curl", url]) —— 输出直接刷到终端,无法检查内容,超时也不中断。
shell=True 是安全漏洞高发区,除非你明确需要 shell 特性
启用 shell=True 后,整个字符串交给系统 shell 解析,用户输入若未清洗,;、&&、$() 都可能被注入执行。
比如:subprocess.run(f"cat {user_input}", shell=True),当 user_input 是 "file.txt; rm -rf /",后果严重。
- 95% 的场景应禁用
shell=True,改用参数列表 - 只有当你确实依赖重定向(
> out.txt)、管道(| grep)、通配符(*.log)时才考虑开启 - 若必须开启,务必对所有动态部分做白名单校验或
shlex.quote()转义(注意:Windows 不兼容shlex)
异步执行时 stdout/stderr 必须 consume,否则子进程永久阻塞
asyncio.create_subprocess_exec() 返回的 proc 对象,只要设了 stdout=PIPE 或 stderr=PIPE,就必须调用 proc.communicate()(或分别读取 read())。不读,缓冲区一满,子进程就停住不动。
典型陷阱:
- 用
proc.stdout.readline()读一行后,没继续读完或没调communicate()→ 剩余输出滞留,下次再读可能卡死 - 混用
proc.stdout.read()和proc.communicate()→ 后者会再次尝试读,导致 EOF 错误或 hang - 忘记关闭 stdin:
stdin=DEVNULL或显式proc.stdin.close(),否则某些交互式命令(如git commit)会等输入而卡住
最简健壮写法:stdout, stderr = await proc.communicate() —— 它自动处理关闭和 EOF。


















