微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

从 Python 以交互方式运行多个 Bash 命令

如何解决从 Python 以交互方式运行多个 Bash 命令

我刚刚遇到 pexpect 并一直在想办法如何使用它来自动化各种实践,否则我必须在命令外壳中手动填写。

这是一个示例脚本:

import pexpect,sys

child = pexpect.spawn("bash",timeout=60)
child.logfile = sys.stdout
child.sendline("cd /workspace/my_notebooks/code_files")
child.expect('#')
child.sendline('ls')
child.expect('#')
child.sendline('git add .')
child.expect('#')
child.sendline('git commit')
child.expect('#')
child.sendline('git push origin main')
child.expect('Username .*:')
child.sendline(<my_github_username>)
child.expect('Password .*:')
child.sendline(<my_github_password>)
child.expect('#')
child.expect(pexpect.EOF)

(我知道这些特定任务不一定需要 pexpect,只是想了解其最佳实践。)

现在,上述工作。它 cd 到我的本地 repo 文件夹,列出那里的文件,暂存我的提交,并通过身份验证推送到 Github,同时向 Python 标准输出提供实时输出。但我有两个方面想要改进:

首先,我将在 Bash 中运行的每一行之间的 .expect('#') (不需要交互性)有点乏味。 (而且我不确定它是否/为什么它似乎总是有效,无论标准输出中的输出是什么 - 尽管到目前为止它确实如此。)理想情况下,我可以将它们聚集成一个多行字符串并省去所有这些 expect s。有没有更自然的方法自动化脚本的某些部分,例如,一个多行字符串,其中 Bash 命令以 ';' 分隔或“&&”或“||”?

其次,如果您运行上述脚本,您会看到它在 60 秒后超时,然后在 Python 中产生 TimeoutError。虽然 - 假设工作在 60 秒内适合 - 它完成了,我更喜欢(1)不会花费不必要的长时间,(2)不会冒中途切断> 60秒过程的风险,(3)不会结束整个事情给我一个 Python 错误。我们是否可以让它自然结束,即当 shell 进程完成时,它也停止在 Python 中运行? (如果 (2) 和 (3) 可以解决,我可能只需设置一个巨大的 timeout 值 - 但不确定是否有更好的做法。)

重写上面代码的最佳方法是什么?我将这两个问题归为一个问题,因为我猜测通常有一种更好的方法可以使用 pexpect,它可以解决这两个问题(可能还有其他我什至不知道的问题!),并且总的来说我想请教一下完成此类任务的最佳方式。

解决方法

您不需要在每个命令之间等待 #。您可以只发送所有命令并忽略 shell 提示。 shell 缓冲所有输入。

您只需要等待用户名和密码提示,然后是最后一个命令后的最后一个 #

最后还需要发送一个exit命令,否则不会得到EOF。

import pexpect,sys

child = pexpect.spawn("bash",timeout=60)
child.logfile = sys.stdout
child.sendline("cd /workspace/my_notebooks/code_files")
child.sendline('ls')
child.sendline('git add .')
child.sendline('git commit')
child.sendline('git push origin main')
child.expect('Username .*:')
child.sendline(<my_github_username>)
child.expect('Password .*:')
child.sendline(<my_github_password>)
child.expect('#')
child.sendline('exit')
child.expect(pexpect.EOF)

如果您遇到 60 秒超时,您可以使用 timeout=None 禁用此功能。见pexpect timeout with large block of data from child

您也可以在一行中组合多个命令:

import pexpect,timeout=60)
child.logfile = sys.stdout
child.sendline("cd /workspace/my_notebooks/code_files && ls && git add . && git commit && git push origin main')
child.expect('Username .*:')
child.sendline(<my_github_username>)
child.expect('Password .*:')
child.sendline(<my_github_password>)
child.expect('#')
child.sendline('exit')
child.expect(pexpect.EOF)

在命令之间使用 && 可确保在其中任何一个失败时停止。

总的来说,我根本不建议为此使用 pexpect。制作一个可以执行您想要的所有操作的 shell 脚本,然后通过一次 subprocess.Popen() 调用运行该脚本。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。