运行 多个 Bash 命令以交互方式来自 Python
Running multiple Bash commands interactively from Python
我刚刚遇到 pexpect
并且一直在弄清楚如何使用它来自动执行各种操作,否则我将不得不在命令中手动填写 shell。
这是一个示例脚本:
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 stdout 提供实时输出。但我有两个方面需要改进:
首先,.expect('#')
在 Bash 中 运行 的每一行之间(不需要交互)有点乏味。 (而且我不确定它是否/为什么它似乎总是有效,无论 stdout 中的输出是什么 - 尽管到目前为止它确实有效。)理想情况下,我可以将它们聚集成一个多行字符串并省去所有这些 expect
秒。难道没有更自然的方法来自动化脚本的某些部分,例如,一个多行字符串,其中 Bash 命令由 ';' 分隔或“&&”或“||”?
其次,如果您 运行 像上面这样的脚本,您会看到它在 60 秒后突然超时,然后在 Python 中产生 TimeoutError。虽然 - 假设工作在 60 秒内完成 - 它完成了,但我更喜欢(1)不会花费不必要的时间,(2)不会冒中途中断> 60秒的过程的风险,(3)不会'不要结束整个事情给我 Python 中的错误。我们能否让它自然结束,即,当 shell 进程完成时,也就是它在 Python 中停止 运行ning 的时候? (如果 (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, sys
child = pexpect.spawn("bash", 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()
调用。
我刚刚遇到 pexpect
并且一直在弄清楚如何使用它来自动执行各种操作,否则我将不得不在命令中手动填写 shell。
这是一个示例脚本:
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 stdout 提供实时输出。但我有两个方面需要改进:
首先,.expect('#')
在 Bash 中 运行 的每一行之间(不需要交互)有点乏味。 (而且我不确定它是否/为什么它似乎总是有效,无论 stdout 中的输出是什么 - 尽管到目前为止它确实有效。)理想情况下,我可以将它们聚集成一个多行字符串并省去所有这些 expect
秒。难道没有更自然的方法来自动化脚本的某些部分,例如,一个多行字符串,其中 Bash 命令由 ';' 分隔或“&&”或“||”?
其次,如果您 运行 像上面这样的脚本,您会看到它在 60 秒后突然超时,然后在 Python 中产生 TimeoutError。虽然 - 假设工作在 60 秒内完成 - 它完成了,但我更喜欢(1)不会花费不必要的时间,(2)不会冒中途中断> 60秒的过程的风险,(3)不会'不要结束整个事情给我 Python 中的错误。我们能否让它自然结束,即,当 shell 进程完成时,也就是它在 Python 中停止 运行ning 的时候? (如果 (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, sys
child = pexpect.spawn("bash", 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()
调用。