scp 功能很強大,但需要人工輸入 password, 當然可以通過把 公鑰保存在遠程主機的 ~/.ssh 目錄中,而后就不用輸入password,但這需要配置.
用 sshpass 可能在命令輸入 password, 但 需要用 “sudo apt-get install sshpass” 安裝
如果不想用上面兩種方法,可以用 expect 編寫腳本可以幫助我們自動交互
雖然 python 也提供 pexpect 模塊,但既然 expect 很簡單,為何不直接用 os.system() 去執行呢?
下面是我編寫的類,實現了遠程復制
- class RemoteShell:
- def __init__(self, host, user, pwd):
- self.host = host
- self.user = user
- self.pwd = pwd
- def put(self, local_path, remote_path):
- scp_put = '''
- spawn scp %s %s@%s:%s
- expect "(yes/no)?" {
- send "yes\r"
- expect "password:"
- send "%s\r"
- } "password:" {send "%s\r"}
- expect eof
- exit'''
- os.system("echo '%s' > scp_put.cmd" % (scp_put % (os.path.expanduser(local_path), self.user, self.host, remote_path, self.pwd, self.pwd)))
- os.system('expect scp_put.cmd')
- os.system('rm scp_put.cmd')
但發現每次文件都沒有復制完,我想看expect 究竟做了什么事情,還好 expect 提供 -d 參數給出更多的信息。
最后發現是被復制文件太大,expect 超時退出了
在腳本前加入 “set timeout -1" 就OK了
- scp_put = '''
- set timeout -1
- spawn scp %s %s@%s:%s
- expect "(yes/no)?" {
- send "yes\r"
- expect "password:"
- send "%s\r"
- } "password:" {send "%s\r"}
- expect eof
- exit'''
總結
1) expect 每一條語句都是順序執行
因為scp 可能先返回 (yes/no)? 再 返回 password:, 也可能直接返回password:, 考慮順序關系,上面語句的層次關系其實如下:
- expect "(yes/no)?" { send "yes\r"
- expect "password:"
- send "%s\r"
- }
- "password:" {send "%s\r"}
2) 每當 spawn 的程序有輸出的時候的,expect都會去匹配, 如果匹配不上,就等下次有輸出,再次執行當前的 expect, 直到超時 (我用 expect -d 去追蹤,得到的結論);當然可以設置沒有超時 "set timeout -1"
3) 如果 expect 退出, 被它 spawn 的程序會被 kill 掉
4) spawn 結束的時候,它向標准輸出的的 eof 會被 expect 檢測到,正好作為 expect 腳本退出的時機。
對於 scp 可以先檢測 100%,因為 scp 會輸出復制進度,再檢測 eof
- expect "100%%"
- expect eof
5) expect 是部分匹配,所以不要擔心自己不知道程序的完整輸出