我正在尝试使用subprocess.Popen
运行scp
(安全副本)命令。登录需要我发送密码:
from subprocess import Popen, PIPE
proc = Popen(['scp', "user@10.0.1.12:/foo/bar/somefile.txt", "."], stdin = PIPE)
proc.stdin.write(b'mypassword')
proc.stdin.flush()
这会立即返回错误:
user@10.0.1.12's password:
Permission denied, please try again.
我某些密码是否正确。我可以通过在shell上手动调用scp
来轻松验证它。那么为什么这不起作用呢?
请注意,有许多类似的问题,询问subprocess.Popen
并为自动SSH或FTP登录发送密码:
How can I set a users password in linux from a python script?
Use subprocess to send a password
这些问题的答案不起作用和/或不适用,因为我使用的是Python 3。
答案 0 :(得分:10)
以下是ssh
使用pexpect
密码的函数:
def ssh(host, cmd, user, password, timeout=30, bg_run=False):
"""SSH'es to a host using the supplied credentials and executes a command.
Throws an exception if the command doesn't return 0.
bgrun: run command in the background"""
fname = tempfile.mktemp()
fout = open(fname, 'w')
options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no'
if bg_run:
options += ' -f'
ssh_cmd = 'ssh %s@%s %s "%s"' % (user, host, options, cmd)
child = pexpect.spawn(ssh_cmd, timeout=timeout)
child.expect(['password: '])
child.sendline(password)
child.logfile = fout
child.expect(pexpect.EOF)
child.close()
fout.close()
fin = open(fname, 'r')
stdout = fin.read()
fin.close()
if 0 != child.exitstatus:
raise Exception(stdout)
return stdout
使用scp
可以实现类似的功能。
答案 1 :(得分:6)
您链接的第二个答案表明您使用Pexpect(这通常是与期望输入的命令行程序交互的正确方法)。它有一个fork适用于你可以使用的python3。
答案 2 :(得分:6)
OpenSSH scp
实用程序调用{{1}}程序与远程主机建立SSH连接,ssh进程处理身份验证。 ssh
实用程序不接受命令行或其标准输入上的密码。我相信这是OpenSSH开发人员的一个深思熟虑的决定,因为他们认为人们应该使用更安全的机制,如基于密钥的身份验证。任何调用ssh的解决方案都将采用以下方法之一:
ssh
来获取密码ssh
的修改版本,其工作方式符合您的要求。在这种特殊情况下,鉴于您已经从python脚本调用ssh
,似乎其中一个是最合理的方法:
答案 3 :(得分:4)
Pexpect有一个完整的库:pxssh
http://pexpect.readthedocs.org/en/stable/api/pxssh.html
import pxssh
import getpass
try:
s = pxssh.pxssh()
hostname = raw_input('hostname: ')
username = raw_input('username: ')
password = getpass.getpass('password: ')
s.login(hostname, username, password)
s.sendline('uptime') # run a command
s.prompt() # match the prompt
print(s.before) # print everything before the prompt.
s.logout()
except pxssh.ExceptionPxssh as e:
print("pxssh failed on login.")
print(e)
答案 4 :(得分:1)
我猜一些应用程序使用stdin与用户交互,一些应用程序使用终端进行交互。在这种情况下,当我们使用PIPE编写密码时,我们正在写入stdin。但SCP应用程序从终端读取密码。由于子进程无法与使用终端的用户交互,但只能使用stdin进行交互,我们无法使用子进程模块,我们必须使用pexpect来使用scp复制文件。
随意更正。
答案 5 :(得分:0)
这是我基于pexpect的scp函数。除密码外,它还可以处理通配符(即多文件传输)。 要处理多个文件传输(即通配符),我们需要通过shell发出命令。请参阅pexpect FAQ。
import pexpect
def scp(src,user2,host2,tgt,pwd,opts='',timeout=30):
''' Performs the scp command. Transfers file(s) from local host to remote host '''
cmd = f'''/bin/bash -c "scp {opts} {src} {user2}@{host2}:{tgt}"'''
print("Executing the following cmd:",cmd,sep='\n')
tmpFl = '/tmp/scp.log'
fp = open(tmpFl,'wb')
childP = pexpect.spawn(cmd,timeout=timeout)
try:
childP.sendline(cmd)
childP.expect([f"{user2}@{host2}'s password:"])
childP.sendline(pwd)
childP.logfile = fp
childP.expect(pexpect.EOF)
childP.close()
fp.close()
fp = open(tmpFl,'r')
stdout = fp.read()
fp.close()
if childP.exitstatus != 0:
raise Exception(stdout)
except KeyboardInterrupt:
childP.close()
fp.close()
return
print(stdout)
可以这样使用:
params = {
'src': '/home/src/*.txt',
'user2': 'userName',
'host2': '192.168.1.300',
'tgt': '/home/userName/',
'pwd': myPwd(),
'opts': '',
}
scp(**params)