如何解决pexpect接入tmux会话后无法发送命令的问题?
我正在编写一个Python脚本,需要通过SSH登录服务器并在tmux会话中执行操作,简化后的脚本如下:
#!/usr/bin/python import pexpect child = pexpect.spawn ('ssh johndoe@1.1.1.1 -p 1111') child.expect ('Password: ') child.sendline (password) child.expect ('Welcome to your .*') child.sendline('tmux') child.sendline('ls -la')
脚本在进入tmux会话前运行正常,但进入会话后无法发送后续的ls -la命令。我尝试了child.expect('\n')、child.expect('$')等匹配值,甚至使用sleep(2),均无法成功在tmux会话中发送命令。请问实现该需求的最佳实践是什么?
这个问题其实很典型——当你进入tmux会话后,pexpect需要匹配的是tmux会话内部的提示符,而不是SSH登录后的系统提示符,而且直接sendline容易因为tmux初始化未完成导致命令失效。这里给你几个可靠的解决思路:
方法1:直接通过SSH命令完成tmux操作(最推荐)
没必要先进入tmux再发命令,直接让远程服务器启动tmux并执行指定操作,这是最简洁稳定的方式:
#!/usr/bin/python import pexpect # 用ssh直接触发tmux创建会话+执行命令,C-m模拟回车键 child = pexpect.spawn('ssh johndoe@1.1.1.1 -p 1111 "tmux new-session -d -s my_temp_session; tmux send-keys -t my_temp_session \'ls -la\' C-m"') child.expect('Password: ') child.sendline(password) # 等待命令执行结束 child.expect(pexpect.EOF)
这种方式完全绕开了tmux内部的交互复杂性,直接通过tmux的命令行参数完成任务,可靠性拉满。
方法2:精准匹配tmux内的提示符再发命令
如果你确实需要进入tmux做交互式操作,核心是要准确捕捉tmux加载完成后的提示符。tmux启动后的提示符通常和系统原有提示符一致,但可能会带会话名前缀(比如[mysession] user@host$),你可以先打印输出确认匹配目标:
#!/usr/bin/python import pexpect child = pexpect.spawn ('ssh johndoe@1.1.1.1 -p 1111') child.expect ('Password: ') child.sendline (password) child.expect ('Welcome to your .*') # 发送tmux命令 child.sendline('tmux') # 匹配tmux内的实际提示符(比如你的系统提示符是user@host$,就用r'\$ ') # 注意用原始字符串避免转义问题 child.expect(r'\$ ') # 现在发送命令就会生效了 child.sendline('ls -la') # 匹配命令执行后的提示符,确认操作完成 child.expect(r'\$ ') # 打印命令输出查看结果 print(child.before.decode())
重点提醒:永远不要依赖sleep()来等待——不同服务器性能差异大,sleep的时间完全不可控,用expect匹配明确的标记才是可靠的做法。
方法3:用interact()调试定位问题
如果还是搞不清提示符是什么样的,可以在发送tmux命令后调用child.interact(),手动进入交互模式,看看实际的输出和提示符状态,这样就能精准确定需要匹配的内容:
child.sendline('tmux') child.interact() # 这里会让你手动操作,观察tmux内的真实状态
内容的提问来源于stack exchange,提问作者ThomasWest

