使用sshpass expect实现非交互登录ssh远程执行命令

有时侯,利用 ssh 在本地执行远程机器的命令可以便捷地处理某些重复工作。我们希望做到:

  • 免手工输入密码
  • 支持执行多个命令,执行 shell 脚本
  • 支持执行 sudo 的命令

免手工输入密码

我们可以使用 ssh 互信,sshpass 和 expect 等工具来避免手工输密码。使用过程可能会碰到如下需要手工输入 yes 的繁琐场景:

$ ssh username@hostname
The authenticity of host ... can't be established.
ECDSA key fingerprint is ...
Are you sure you want to continue connecting (yes/no)?

为了避免出现上述场景,往 ssh 命令添加如下参数:

$ ssh -o "StrictHostKeyChecking no" username@password

SSH 互信

SSH 互信的配置非常简单,首先生成 ssh key:

$ ssh-keygen

把 public key 拷贝到信任方中:

$ ssh-copy-id -i ~/.ssh/id_rsa.pub username@hostname

之后免密执行命令:

$ ssh -o "StrictHostKeyChecking no" username@password cmd

sshpass

sshpass 是一个用于非交互的 ssh 密码验证工具,使用前先安装:

$ yum install sshpass

使用如下:

$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@hostname cmd

expect

Expect 是用来进行自动化控制和测试的软件工具。虽然学习成本较高,但是 expect 的功能强大,利用 expect 可以方便的执行远程命令。使用前先安装:

$ yum install expect

例如:

#!/usr/bin/expect

spawn ssh -o "StrictHostKeyChecking no" username@hostname
expect "*assword*"
send "passwordn"
expect "*$*"
send "commandn"
expect "*$*"
send "exitn"
expect eof

Expect 不仅支持 ssh,还支持 scp, ftp 等工具。

支持多命令和脚本

执行多条命令

sshpass 和 expect 在支持多条命令上非常类似,只需用 && 连接命令即可:

# ssh trust
$ ssh -o "StrictHostKeyChecking no" username@password "cmd1 && cmd2"

例如:

# sshpass
$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password "ls -a && mkdir test"

# expect
......
expect "*$*"
send "ls -a && mkdir testn"
......

执行本地脚本

对于执行本地脚本,ssh 和 sshpass 的用法类似。

# ssh trust
$ ssh -o "StrictHostKeyChecking no" username@password bash -s < shell_script.sh

# sshpass
$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password bash -s < shell_script.sh

对于 expect,首先需要把脚本拷贝到远程主机,然后在远程主机执行该脚本,步骤如下:

...
# Copy script to remote host
spawn scp -o "StrictHostKeyChecking no" shell_script.sh username@hostname:~/
expect "*assword*"
send "passwordn"
expect "*100%*"
expect eof

# Execute the shell script at remote host
spawn ssh -o "StrictHostKeyChecking no" username@hostname
expect "*assword*"
send "passwordn"
expect "*$*"
send "sh shell_script.shn"
......

支持执行 sudo 命令

有些命令需要 sudo 权限才能执行,但是我们不希望重复的输入密码,我们可以把每条命令修改为如下:

cmd ---> 'echo password | sudo -S cmd'

例如:

$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password "echo password | sudo -S mkdir /newdir"

对于如 echo, dd 等部分命令,有时会出现如下失败场景:

$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password 'echo password | sudo -S echo hello > /newdir/newfile'
bash: /newdir/newfile: 权限不够

解决办法如下:

cmd ---> 'echo password | sudo -S sh -c "cmd"'

# For example
$ sshpass -p password ssh -o "StrictHostKeyChecking no" username@password 'echo WSfdl097018= | sudo -S sh -c "echo hello >  /newdir/newfile"'

如果采用 expect,需要把脚本拷贝到远程主机,然后在远程主机采用 sudo 执行该脚本,相对 sshpass 更简便和健壮:

...
# Copy script to remote host
spawn scp -o "StrictHostKeyChecking no" shell_script.sh username@hostname:~/
expect "*assword*"
send "passwordn"
expect "*100%*"
expect eof

# Execute the shell script at remote host
spawn ssh -o "StrictHostKeyChecking no" username@hostname
expect "*assword*"
send "passwordn"
expect "*$*"
send "sudo sh shell_script.shn"
expect "*assword*"
send "passwordn"
......

同时管理多台服务器的expect脚本

最近通过exploring expect书籍,简单学了下expect脚本语言,这个脚本语言是tcl语言的扩展,用来解决一些工具无法自动交互的问题,如ssh登录时,无法在命令就指定密码等。下面是利用expect来实现管理多台服务器的简单例子:

  1. #!/usr/bin/expect
  2. #purpose:auto run command on multiple servers
  3. #how to:  mms <user> <cmd>
  4. #write by zhumaohai.
  5. #blog:http://devops.webres.wang/
  6.  
  7. if {$argc < 2} {
  8. puts "usage: mms <user> <cmd>"
  9. exit 1
  10. }
  11.  
  12. #set servers
  13. set SERVERS {"192.168.0.100" "192.168.0.101" "192.168.0.102"}
  14.  
  15. #set password
  16. set PASSWORDS(user1) "passwd1"
  17. set PASSWORDS(user2) "passwd2"
  18.  
  19. #get virables
  20. set USER [lindex $argv 0]
  21. set CMD [lrange $argv 1 end]
  22.  
  23. set passwd $PASSWORDS($USER)
  24.  
  25. foreach x $SERVERS {
  26. eval spawn ssh -l $USER $x $CMD
  27. expect {
  28. "password" { send "$passwdr" }
  29. "yes/no" { send "yesr";exp_continue; }
  30. }
  31. expect eof
  32. }

1、这里定义了三台服务器192.168.0.100 192.168.0.101 192.168.0.102,定义了用户user1的密码为passwd1,用户user2的密码为passwd2,假如脚本文件名为ms,用法为:
./ms 用户 命令
如./ms user1 date
2、在使用脚本时,请确认系统已经安装有expect命令,centos使用yum install expect安装,ubuntu使用apt-get install expect安装。

expect spawn、linux expect 用法小记

使用expect实现自动登录的脚本,网上有很多,可是都没有一个明白的说明,初学者一般都是照抄、收藏。可是为什么要这么写却不知其然。本文用一个最短的例子说明脚本的原理。
  脚本代码如下:

  1.   ##############################################
  2.   #!/usr/bin/expect
  3.   set timeout 30
  4.   spawn ssh -l username 192.168.1.1
  5.   expect "password:"
  6.   send "ispassr"
  7.   interact
  8.   ##############################################

  1. [#!/usr/bin/expect]
  这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。
  注意:这一行需要在脚本的第一行。
  2. [set timeout 30]
  基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒
  3. [spawn ssh -l username 192.168.1.1]
  spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。
  它主要的功能是给ssh运行进程加个壳,用来传递交互指令。
  4. [expect “password:”]
  这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒
  5. [send “ispassr”]
  这里就是执行交互动作,与手工输入密码的动作等效。
  温馨提示: 命令字符串结尾别忘记加上“r”,如果出现异常等待的状态可以核查一下。
  6. [interact]
  执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行

  1.   #!/usr/bin/expect #注意安装的路径,不确定 whereis expect 一下
  2.   # Change a login shell to bash
  3.   set user [lindex $argv 0]
  4.   spawn bash $user
  5.   expect "]:"
  6.   send "/bin/bash "
  7.   expect eof
  8.   exit

转自:http://sysop.blogbus.com/logs/70787883.html

使用expect实现scp ssh自动输入密码登录

expect是一种自动交互语言,能实现在shell脚本中为scp和ssh等自动输入密码自动登录。
下面给出scp和ssh的使用示例:
1、scp

  1. expect -c "
  2.   spawn scp [email protected]:/root/1.log /root
  3.   expect {
  4.     "*assword" {set timeout 300; send "passwordr";}
  5.     "yes/no" {send "yesr"; exp_continue;}
  6.   }
  7.   expect eof"

2、ssh

  1. #!/bin/bash
  2. expect -c "
  3. spawn ssh [email protected] "ls;"
  4. expect {
  5.     "*assword" {set timeout 300; send "passwordr";}
  6.     "yes/no" {send "yesr"; exp_continue;}
  7.       }
  8. expect eof
  9.             "