如何在Python中链接多个命令行响应?

Mic*_*wol 6 python shell command-line

我一直在尝试在另一个python脚本中运行脚本而没有运气.

我试图运行的脚本是garminbackup.py,可以在以下repo中找到.

https://github.com/petergardfjall/garminexport

当您从命令行运行此脚本时,它会要求您提供一些信息,用户名,保存文件的位置等等...

command = 'python garminbackup.py --backup-dir=Name email --format tcx'
Run Code Online (Sandbox Code Playgroud)

然后它要求输入密码,一旦输入密码,就开始将garmin文件下载到提供的目录中.我的目标是创建一个程序,它循环遍历多个帐户并更新每个文件夹中的数据.

我的问题是我似乎无法在python中获得子进程模块来实现这一点.

我没有运气就尝试了以下命令.它似乎卡在输入密码输入屏幕上,并没有做任何其他事情.

import subprocess,shlex
from subprocess import PIPE,call
import os

p = subprocess.Popen(command,stdout=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)
p.stdin.write("password\n")
p.wait()
Run Code Online (Sandbox Code Playgroud)

我搜索了几个小时,运气不好.任何帮助表示赞赏.

Sam*_*ner 1

尽管OP通过使用该--password选项解决了这个问题,但其他人可能会偶然发现同样的问题,并且正如HuStmpHrr已经指出的那样,在命令行中传递密码不是一个好主意。

为什么它不起作用

在本例中,被调用的脚本garminbackup.py使用getpass。正如文档中所暗示的, getpass 的实现比仅仅读取 stdin 更复杂:

[...] 在 Unix 上,提示符被写入类似文件的对象流中。流默认为控制终端 (/dev/tty),或者如果 sys.stderr 不可用(此参数在 Windows 上被忽略)。

如果无回显输入不可用,则 getpass() 会回退到打印警告消息以进行流式传输并从 sys.stdin 读取并发出 GetPassWarning。

它应该如何运作

我们可以模拟一个虚拟终端,但如果没有库,那就太复杂了(参见下一章)。

另一种方法是删除存在终端的信息(“如果无回声输入不可用”)。结果比我预期的要难,通常运行一个命令cat | command | cat应该摆脱 tty 检测,但似乎 getpass 改变其行为要么太聪明,要么太无知。

更好的方法:pexpect

正如LohmarASHAR 已经提到的,如果您可以使用pexpect ( pip install pexpect),您就应该使用。它正是为此而设计的,并将涵盖所有极端情况。

例如:

child = pexpect.spawn('scp foo user@example.com:.')
child.expect('Password:')
child.sendline(mypassword)
Run Code Online (Sandbox Code Playgroud)

这甚至适用于要求密码或 普通 stdio 流之外的其他输入的命令。例如,ssh 直接从 TTY 设备读取输入,绕过 stdin。

继续使用 OP 的示例,但使用 pexpect:

#!/usr/bin/env python

import pexpect

command = 'python garminbackup.py --backup-dir=Name email --format tcx'

p = pexpect.spawn(command)  # instead of subprocess.Popen
p.expect(":")               # This is waiting for the prompt "Enter password:", but I rather only use ":" to cope with internationalisation issues
p.sendline("password")
print p.read()              # Output the sub-processes output 
p.wait()
Run Code Online (Sandbox Code Playgroud)