我有很多服务器使用相同的密码。 我想在我的所有服务器中scp相同的文件。 为此,我写了脚本,但无法正常工作? 这是对的吗?我怎么能这样做?
#!/usr/bin/env bash
export HISTFILE=
IP1="1.1.1.1
2.2.2.2
3.3.3.3
4.4.4.4"
IP2="5.5.5.5
6.6.6.6
6.6.6.6
7.7.7.7"
DIR1="1
2
3
4"
DIR2="5
100
104
135"
for ip1 in $IP1
do
sshpass -fpass1.txt scp -r root@$IP1:/attach/res* /user/path/to/$DIR1/
done
for ip2 in $IP2
do
sshpass -fpass2.txt scp -r root@$IP2:/attach/res* /user/path/to/$DIR2/
done
结果是:
107: No such file or directory
107: No such file or directory
107: No such file or directory
107: No such file or directory
135: No such file or directory
135: No such file or directory
135: No such file or directory
135: No such file or directory
我不知道怎么可能 提前谢谢你
答案 0 :(得分:3)
以下是一些建议:
set -xv
来帮助您调试shell脚本。只需将set -xv
放在要调试的部分之前,然后set +xv
关闭调试。您还可以使用export PS4="\$LINENO> "
在shell脚本中打印行号。以下是您使用set -xv
查看脚本的方式。这是调试shell脚本的一个非常好的工具。使用它:
export PS4="\$LINENO: "
set -xv
for ip1 in $IP1
do
sshpass -fpass1.txt scp -r root@$IP1:/attach/res* /user/path/to/$DIR1/
done
for ip2 in $IP2
do
sshpass -fpass2.txt scp -r root@$IP2:/attach/res* /user/path/to/$DIR2/
done
set +xv
我只是编写了sshpass
命令echo语句,因为程序无论如何都无法在我的系统上运行。这是echo
的输出。这是您尝试执行的命令:
sshpass -fpass1.txt scp -r root@1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4:/attach/res* /user/path/to/1 2 3 4/
sshpass -fpass1.txt scp -r root@1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4:/attach/res* /user/path/to/1 2 3 4/
sshpass -fpass1.txt scp -r root@1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4:/attach/res* /user/path/to/1 2 3 4/
sshpass -fpass1.txt scp -r root@1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4:/attach/res* /user/path/to/1 2 3 4/
sshpass -fpass2.txt scp -r root@5.5.5.5 6.6.6.6 6.6.6.6 7.7.7.7:/attach/res* /user/path/to/5 100 104 135/
sshpass -fpass2.txt scp -r root@5.5.5.5 6.6.6.6 6.6.6.6 7.7.7.7:/attach/res* /user/path/to/5 100 104 135/
sshpass -fpass2.txt scp -r root@5.5.5.5 6.6.6.6 6.6.6.6 7.7.7.7:/attach/res* /user/path/to/5 100 104 135/
sshpass -fpass2.txt scp -r root@5.5.5.5 6.6.6.6 6.6.6.6 7.7.7.7:/attach/res* /user/path/to/5 100 104 135/
首先,该IP地址全是错误的。您注意到 似乎 要循环,因为命令正在执行您期望的次数,但您在命令中使用了错误的变量作为IP地址。你有:
sshpass -fpass1.txt scp -r root@$IP1:/attach/res* /user/path/to/$DIR1/
^^^^
而不是:
sshpass -fpass1.txt scp -r root@$ip1:/attach/res* /user/path/to/$DIR1/
^^^^
请注意,$ip1
是您的循环变量,$IP1
是包含所有IP地址的变量。 请勿使用大小写相同的名称调用变量! 。如果你给他们每个不同的名字会更好。例如$ip_list1
而不是$IP1
和$ip
而不是$ip1
:
ip_list1="..."
for ip in $ip_list`
do
sshpass -fpass1.txt scp -r root@$ip:/attach/res* /user/path/to/$DIR1/
done
纠正这个问题,我得到:
sshpass -fpass1.txt scp -r root@1.1.1.1:/attach/res* /user/path/to/1
2
3
4/
sshpass -fpass1.txt scp -r root@2.2.2.2:/attach/res* /user/path/to/1
2
3
4/
sshpass -fpass1.txt scp -r root@3.3.3.3:/attach/res* /user/path/to/1
2
3
4/
sshpass -fpass1.txt scp -r root@4.4.4.4:/attach/res* /user/path/to/1
2
3
4/
sshpass -fpass2.txt scp -r root@5.5.5.5:/attach/res* /user/path/to/5
100
104
135/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/5
100
104
135/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/5
100
104
135/
sshpass -fpass2.txt scp -r root@7.7.7.7:/attach/res* /user/path/to/5
100
104
135/
现在看到问题?您所在的scping
目录不正确。看起来您希望将文件发送到多个目录,并假设scp
命令以某种方式循环自身。
这是我的程序版本。我添加了一个内部循环来遍历每个IP地址的每个目录:
#!/bin/bash
ip_list1="1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4"
ip_list2="5.5.5.5 6.6.6.6 6.6.6.6 7.7.7.7"
dir_list1="1 2 3 4"
dir_list2="5 100 104 135"
for ip in $ip_list1
do
for dir in $dir_list1
do
echo "sshpass -fpass1.txt scp -r root@$ip:/attach/res* /user/path/to/$dir/"
done
done
for ip in $ip_list2
do
for dir in $dir_list2
do
echo "sshpass -fpass2.txt scp -r root@$ip:/attach/res* /user/path/to/$dir/"
done
done
您不需要在多行上设置IP列表和目录列表,因为for
会破坏空白处的变量。这既是一种祝福,也是一种诅咒。或者,如果你不小心,更好的可能会让你诅咒。您必须确保没有意外的空格。
输出现在更合理:
sshpass -fpass1.txt scp -r root@1.1.1.1:/attach/res* /user/path/to/1/
sshpass -fpass1.txt scp -r root@1.1.1.1:/attach/res* /user/path/to/2/
sshpass -fpass1.txt scp -r root@1.1.1.1:/attach/res* /user/path/to/3/
sshpass -fpass1.txt scp -r root@1.1.1.1:/attach/res* /user/path/to/4/
sshpass -fpass1.txt scp -r root@2.2.2.2:/attach/res* /user/path/to/1/
sshpass -fpass1.txt scp -r root@2.2.2.2:/attach/res* /user/path/to/2/
sshpass -fpass1.txt scp -r root@2.2.2.2:/attach/res* /user/path/to/3/
sshpass -fpass1.txt scp -r root@2.2.2.2:/attach/res* /user/path/to/4/
sshpass -fpass1.txt scp -r root@3.3.3.3:/attach/res* /user/path/to/1/
sshpass -fpass1.txt scp -r root@3.3.3.3:/attach/res* /user/path/to/2/
sshpass -fpass1.txt scp -r root@3.3.3.3:/attach/res* /user/path/to/3/
sshpass -fpass1.txt scp -r root@3.3.3.3:/attach/res* /user/path/to/4/
sshpass -fpass1.txt scp -r root@4.4.4.4:/attach/res* /user/path/to/1/
sshpass -fpass1.txt scp -r root@4.4.4.4:/attach/res* /user/path/to/2/
sshpass -fpass1.txt scp -r root@4.4.4.4:/attach/res* /user/path/to/3/
sshpass -fpass1.txt scp -r root@4.4.4.4:/attach/res* /user/path/to/4/
sshpass -fpass2.txt scp -r root@5.5.5.5:/attach/res* /user/path/to/5/
sshpass -fpass2.txt scp -r root@5.5.5.5:/attach/res* /user/path/to/100/
sshpass -fpass2.txt scp -r root@5.5.5.5:/attach/res* /user/path/to/104/
sshpass -fpass2.txt scp -r root@5.5.5.5:/attach/res* /user/path/to/135/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/5/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/100/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/104/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/135/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/5/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/100/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/104/
sshpass -fpass2.txt scp -r root@6.6.6.6:/attach/res* /user/path/to/135/
sshpass -fpass2.txt scp -r root@7.7.7.7:/attach/res* /user/path/to/5/
sshpass -fpass2.txt scp -r root@7.7.7.7:/attach/res* /user/path/to/100/
sshpass -fpass2.txt scp -r root@7.7.7.7:/attach/res* /user/path/to/104/
sshpass -fpass2.txt scp -r root@7.7.7.7:/attach/res* /user/path/to/135/
删除echo
,现在应该可以了。
答案 1 :(得分:2)
看来你希望Bash在多行变量的时候循环一行,但这根本不是它的工作方式。字符串是一个字符串,其中包含换行符的字符串仍然只是一个字符串。你可以在一个不带引号的字符串中循环遍历标记(单词)但是没有简单的方法可以索引到相同长度的字符串,就像你显然想要的那样(隐式)。
您可以改为使用Bash数组:
IP1=( 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4 )
DIR1=( 1 2 3 4 )
for idx in 0 1 2 3; do
sshpass -fpass1.txt scp -r root@"${IP1[$idx]}":/attach/res* /user/path/to/"${DIR1[$idx]}"
done
或者您可以使用简单的参数列表和此处的文档:
while read ip dir; do
sshpass -fpass1.txt scp -r root@$ip:/attach/res* /user/path/to/$dir/
done <<____HERE
1.1.1.1 1
2.2.2.2 2
3.3.3.3 3
4.4.4.4 4
____HERE
我更倾向于选择后一种方法,因为它更具可读性和便携性。如果你想更新列表,服务器IP和路径保持在一起,所以你不必跟踪每个数组的索引(这对于包含四个元素的数组来说是微不足道的,但真正麻烦的是更多你长大了六个左右,和/或超过两三个阵列)。这种语法可以一直移植到原始的v7 Bourne shell,所以它适用于(现代版本)Bash不可用的地方。
(为了您的娱乐,请参阅如何使用数组实现以下内容。
while read user home shell groups loc; do
cat <<-____HERE
User: $user
Home: $home
Shell: $shell
Groups: $groups
Loc: $loc
____HERE
done <<HERE
frodo /home/shire /bin/bash dev Mordor
bilbo /home/valinor /bin/ksh admin,dev Undying Lands
maisy /home/maisy /bin/zsh dev Mouseville
tallulah /home/tallulah /bin/sh wheel,admin,dev,deity Dekadenz Kave
HERE
...尤其是当您添加更多用户时。或许不这样做。)
您可以使用公钥(我建议这样做)从中分解sshpass
,但我想您仍然需要以这种或那种方式使用此脚本的逻辑。
答案 2 :(得分:1)
Create a key pair并将公钥分发给您的服务器。如果您在私钥上输入密码,那么在加载密钥时您需要ssh-agent
运行。