如何在Discord bot中调用外部脚本并将其逐行控制台输出发送到频道
问题解决方法
问题原因
当前使用的subprocess.run()是阻塞式同步调用,存在两个核心问题:
- 必须等待被调用脚本完全执行结束后才会统一返回所有输出,无法实现实时逐行获取
capture_output=True会捕获子进程的所有标准输出/错误流到变量中,自然不会打印到控制台
同时现有代码存在缩进错误:消息判断逻辑不在on_message回调函数内部,运行时无法触发对应逻辑。
解决方案
改用asyncio异步子进程接口执行脚本,既不会阻塞Discord bot的事件循环,也可以实现逐行实时读取输出,同时兼顾控制台打印和Discord消息发送。
修改后完整代码:
import discord import asyncio TOKEN = 'TOKEN(已隐去)' # 命令映射白名单,避免用户输入恶意路径执行任意脚本,按需配置 CMD_MAP = { "script1": "./path/to/script1.py", "script2": "./path/to/script2.py" } # 注意:discord.py v2.x以上版本需要显式声明intents,同时要在Discord开发者后台开启对应权限 client = discord.Client(intents=discord.Intents.all()) @client.event async def on_ready(): print("Logged in as {0.user}".format(client)) @client.event async def on_message(message): username = str(message.author).split("#")[0] usermsg = str(message.content) channel = str(message.channel.name) print(f'{username}, {usermsg}, {channel}') if message.author == client.user: return if message.channel.name == "test" and usermsg.startswith("/"): arg = usermsg[1:].strip() # 校验命令合法性 if arg not in CMD_MAP: await message.channel.send(f"无效命令,支持的命令:{'、'.join(CMD_MAP.keys())}") return await message.channel.send(f'Executing your requested script, {username}') # 异步启动子进程,-u参数关闭Python输出缓冲,保证输出实时返回 proc = await asyncio.create_subprocess_exec( "python3", "-u", CMD_MAP[arg], stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT, # 合并错误流到输出流统一处理 text=True ) # 逐行读取输出 while True: line = await proc.stdout.readline() if not line: break line = line.strip() if not line: continue # 同时打印到控制台和发送到Discord频道 print(line) await message.channel.send(line) # 等待进程完全退出 await proc.wait() await message.channel.send(f"脚本执行结束,退出码:{proc.returncode}") client.run(TOKEN)
注意事项
- 若使用discord.py v2.x及以上版本,需要先在Discord开发者后台开启bot的「Message Content Intent」权限,否则无法读取用户消息内容
- 如果不想给执行命令加
-u参数,也可以在被调用脚本的所有print语句中添加flush=True参数,避免输出缓冲导致无法实时获取内容
内容的提问来源于stack exchange,提问作者il_pow23
相关产品推荐
相关产品推荐

