如何使用Unix pass命令行程序使用Python自动设置密码

时间:2018-07-22 20:46:41

标签: python linux command-line-interface popen

我正在尝试使用Unix pass程序自动设置新密码。 我了解有一个Python库pexpect可能会有所帮助,但我想避免使用第三方库。

使用终端时,流程如下:

$ pass insert --force gmail
>> Enter password for gmail: <type in password using masked prompt>
>> Retype password for gmail: <reenter password>

我希望我的功能做什么

  1. 运行命令pass insert --force {entry_name}
  2. 捕获输出(并回显以进行测试)
  3. 检查输出是否存在“ gmail密码”,如果为True
    • 将“ {password} \ n”写入标准输入
    • 再次写'{password} \ n'到stdin
  4. 回显任何错误或消息以进行测试

问题:

我被困在步骤2上。子进程无限期挂起,错误超时或不产生任何输出。

尝试:

  • 我已经尝试使用stdin.write()和communication()来配置Popen()。
  • 我已经在各个时间点设置了wait()调用。
  • 我尝试了shell = True和shell = False选项(出于安全原因,最好选择False)

代码

def set_pass_password(entry_name, password):
    from subprocess import Popen, PIPE

    command = ['pass', 'insert', '--force', entry_name]

    sub = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)

    # At this point I assume that the command has run, and that there is an "Enter password..." message
    message = sub.stdout.read()  # also tried readline() and readlines()
    print(message) # never happens, because process hangs on stdout.read()

    if 'password for {}'.format(entry_name) in message:
        err, msg = sub.communicate(input='{p}\n{p}\n'.format(p=password))
        print('errors: {}\nmessage: {}'.format(err, msg))

1 个答案:

答案 0 :(得分:1)

编辑:最初的答案是关于passwd的,它用于设置密码。后来我注意到您使用pass,它是一个密钥库(实际上并没有更改Unix密码)。 pass程序的工作方式不同,如果stdin不是tty,将不会打印提示。因此,以下非常简单的程序可以工作:

def set_pass_password(entry_name, password):
    from subprocess import Popen, PIPE

    command = ['pass', 'insert', '--force', entry_name]

    sub = Popen(command, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE)

    err, msg = sub.communicate(input='{p}\n{p}\n'.format(p=password))
    print('errors: {}\nmessage: {}'.format(err, msg))

if __name__ == "__main__":
    set_pass_password("ttt", "ttt123asdqwe")

(如果命令成功,您将看到stderr和stdout均为空)。

对于passwd命令:

仅供参考:passwd命令将提示输出到stderr,而不是stdout

注意:您可能无需在同一“写”中两次发送密码,而是需要等待第二次提示,然后再次发送密码。

对于这种简单情况,类似于您的代码应该可以工作,但是通常应该在所有管道上使用select并在另一侧准备好时发送/接收数据,这样就不会出现死锁。