背景:我必须将文件从一台服务器复制到测试环境中的 100 多台服务器。复制文件后,需要更改/验证文件的权限。这些都是 Linux 服务器。大多数服务器的登录密码相同,但有些可能不同。
我需要帮助在我的 bash 脚本中创建一个循环。它实际上是在调用 Expect。我想“改进”或修复的领域有以下几点
- 需要密码的部分。(* 部分),它应该在 1 次密码尝试失败后退出脚本,并将 IP 转储到名为“fail.txt”的文本文件中
- 理想情况下,如果没有与服务器的连接,还应该有一个部分将 IP 转储到 fail.txt 中。
我尝试编写密码部分,认为它是一个循环,但我不确定这种思维方式是否可行。我尝试添加另一个“expect Password:”,认为如果再次收到提示,则退出,但我很难让它工作。谢谢!
#!/bin/bash
while read ip; do
sleep 2
expect <<- DONE
set timeout 1
spawn scp yoman.txt root@$ip:/felixtemp
if above command fails, dump the IP to fail.txt, otherwise continue
expect yes/no { send yes\r }
expect Password: { send aaaaaa\r } #if this is good, continue the script from *****
else #exit the script
expect Password: { send 033\r }
expect # { send "echo 'password failed'\r" }
&& dump to a text file called fail.txt
***** expect # { send "exit\r\r" }
sleep 1
set timeout 1
spawn ssh root@$ip
sleep 2
expect yes/no { send yes\r }
sleep 2
expect Password: { send aaaaaa\r }
sleep 5
expect # { send "cd /felixtemp\r" }
expect # { send "chown informix:informix yoman.txt\r" }
expect # { send "chmod 775 yoman.txt\r" }
expect # { send "sum yoman.txt | grep 10350 && echo 'transfer good' || echo 'transfer bad'\r" }
expect # { send exit\r }
sleep 1
DONE
done < ip.txt
答案1
如果您必须管理数百台 Linux 服务器,则应使用配置管理工具来执行这些任务。一个非常简单的配置管理工具是 ansible,对管理系统的唯一要求是 python 2.4 或更高版本(http://docs.ansible.com/intro_installation.html#managed-node-requirements)。
您的问题已通过 ansible 解决:
1)定义主机列表,您可以为所有主机定义一个默认密码,并为某些主机定义另一个密码
[hosts_list]
172.17.0.101 ansible_ssh_user=root ansible_ssh_pass=password
172.17.0.102 ansible_ssh_user=root ansible_ssh_pass=oldpassword
172.17.0.103
172.17.0.104
[hosts_list:vars]
ansible_ssh_user=root ansible_ssh_pass=default_password
2)定义一个简单的剧本,其中包含要在托管节点上执行的任务
root@node1:~# cat play.yoman
---
- hosts: hosts_list
tasks:
- name: "Build hosts file"
copy: src=/root/yoman.txt dest=/tmp/felixtemp owner=user group=adm mode=0755
3)执行剧本并检查结果
root@node1:~# ansible-playbook -i hosts_list play.yoman
PLAY [hosts_list] *************************************************************
GATHERING FACTS ***************************************************************
fatal: [172.17.0.104] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
fatal: [172.17.0.103] => SSH encountered an unknown error during the connection. We recommend you re-run the command using -vvvv, which will enable SSH debugging output to help diagnose the issue
ok: [172.17.0.101]
fatal: [172.17.0.102] => Authentication failure.
TASK: [Build hosts file] ******************************************************
ok: [172.17.0.101]
PLAY RECAP ********************************************************************
to retry, use: --limit @/root/play.yoman.retry
172.17.0.101 : ok=2 changed=0 unreachable=0 failed=0
172.17.0.102 : ok=0 changed=0 unreachable=1 failed=0
172.17.0.103 : ok=0 changed=0 unreachable=1 failed=0
172.17.0.104 : ok=0 changed=0 unreachable=1 failed=0
您可以获得已执行任务的服务器列表以及任务失败的服务器列表(原因为无法访问的服务器或密码错误)。您还可以获得文件、其权限和内容未更改的服务器子集,因为它们已更新
答案2
正如 OP 所要求的,先前的评论都没有使用 bash 脚本中的 expect 。
最近,我需要做类似的任务,因此这里是我使用 expect 的解决方案:
expect <<- DONE
set timeout 1
spawn scp yoman.txt root@${ip}:/felixtemp
while 1 {
expect {
"no)?" {
send "yes\n"
}
"denied" {
log_file fail.txt
send_log "Couldn't log in to ${ip}.\n";
exit 1
}
"assword:" {
send "${password}\n"
}
"100%" {
break;
}
}
}
expect eof
DONE
预期语法的解释:
- while 1 { }: 形成一个无限循环。在循环内部,我定义了不同的期望情况。
- log_file fail.txt:创建一个名为fail.txt的文件。
- send_log“text”:将文本发送到log_file。
- break:跳出无限循环并结束期望部分。
答案3
你为什么不使用sshpass
?
环形附有一张表格:
tab=(
1.2.3.4
4.3.2.1
...
);
for (( i = 1; i < ${#tab[*]}; i++ )) {
echo ${tab[i]};
...
}
您需要针对特定 IP 地址的一些条件来设置正确的密码。
SCP
sshpass -p $PASSWORD scp -o StrictHostKeyChecking=no $FILE $USER@$HOST:$PATH
根据手册页,返回值为:
0 成功
1 命令行参数无效
2 给出相互矛盾的论点
3 常见运行时错误
4 无法识别来自 ssh 的响应(解析错误)
5 密码无效/不正确
6 主机公钥未知。sshpass 退出而不确认新密钥。
如果您想在文件中打印 $HOST 并返回以下值,这将很有用:
sshpass -p $PASSWORD scp -o StrictHostKeyChecking=no $FILE $USER@$HOST:$PATH
if [ $? -ne 0 ]
then
echo $HOST:$? >> file.txt;
fi
SSH
发送命令:
sshpass -p $PASSWORD ssh -o StrictHostKeyChecking=no $USER@$HOST $CMD