如何在 Expect/Tcl 中使用数组 ssh?

 2022-02-11 

How to ssh with Arrays in Expect/Tcl?

我可以成功地生成一个 ssh 会话,执行一些 scp 并在 Excpet/Tcl 中执行一个 bash 脚本。但是,当我尝试通过 for 循环和数组 ssh 进入多个系统时,我得到一个 ssh: Could not resolve hostname $SOME_HOST: Temporary failure in name resolution 错误。

我真的很想通过使用数组来做到这一点,但无论出于何种原因,Expect/Tcl 在尝试将数组与 ssh 一起使用时都会遇到问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#arrays
array set userArray {
    0 $A_USERNAME
    1 $A_USERNAME
    2 $A_USERNAME
    3 $B_USERNAME
    4 $B_USERNAME
    5 $A_USERNAME
    6 $A_USERNAME
    7 $A_USERNAME
    8 $A_USERNAME
    9 $A_USERNAME
    10 $B_USERNAME
   #11 $B_USERNAME
}

array set hostArray {
    0 $A_HOST
    1 $B_HOST
    2 $C_HOST
    3 $D_HOST
    4 $E_HOST
    5 $F_HOST
    6 $H_HOST
    7 $I_HOST
    8 $J_HOST
    9 $K_HOST
    10 $L_HOST
   #11 $M_HOST
}

array set sshPasswordArray {
    0"placeholder0"
    1"placeholder1"
    2"placeholder2"
    3"placeholder3"
    4"placeholder4"
    5"placeholder5"
    6"placeholder6"
    7"placeholder7"
    8"placeholder8"
    9"placeholder9"
    10"placeholder10"
   #11"placeholder11"
}


#expect"$" { send --"cd /tmp/\
" }




#This loop will step through every system on a system sshing and then running $LINUX_HARDWARE_COLLECTION_FILE on said system.

for { set index 0 } { $index < $NUM_SYSTEMS_IN_A_SYSTEM } { incr index } {
    puts"In loop: $index"
    spawn ssh -o StrictHostKeyChecking=no -l $userArray($index) $hostArray($index)
    expect"*?assword:"
    send"$sshPasswordArray($index)\
"

   #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}

有谁知道如何成功地做到这一点?问题似乎是 Excpet/Tcl 不想读取我的数组的值。对于上述代码,假设 A_USERNAMEB_USERNAME、各种主机和密码都是有效字符串。


array set 命令,当这样使用时,不会替换那些定义中的变量。例如,hostArray(0) 设置为文字字符 $A_HOST。解决此问题的简单方法是使用 subst 命令在使用前对值进行后处理。

1
2
3
4
5
6
7
8
for { set index 0 } { $index < $NUM_SYSTEMS_IN_A_SYSTEM } { incr index } {
    puts"In loop: $index"
    spawn ssh -o StrictHostKeyChecking=no -l \\
            [subst $userArray($index)] [subst $hostArray($index)]
    expect"*?assword:"
    send"$sshPasswordArray($index)"
   #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}

另外,请记住在密码的 send 末尾放置一个 \
(以模拟按下 Return 键)并且不要忘记在末尾添加 close 生成的子进程环形。 (对于 10 个项目,这通常不是很重要,但是您可以同时打开的虚拟终端数量相当少,并且包括您机器上的所有其他用户;最好将同时打开的数量保持在尽可能小尽可能。)

你也可以在 array set 的位置使用它,但是如果你有带空格的变量,那它可能会失效,所以我真的不推荐它:

1
2
3
4
5
array set userArray [subst {
    0 $A_USERNAME
    1 $A_USERNAME
    ... ...
}]

注意:array set 根本不支持注释格式。您在示例中使用键 #11 进行了条目...


Tcl 中的数组是将任意字符串映射到字符串的关联数组。
Tcl 中的列表是数字索引数组。

你也可以这样做:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
set hostArray [list $A_HOST     $B_HOST     $C_HOST     $D_HOST     $E_HOST     $F_HOST     $H_HOST     $I_HOST     $J_HOST     $K_HOST     $L_HOST     $M_HOST]
set userArray [list $A_USERNAME $A_USERNAME $A_USERNAME $B_USERNAME $B_USERNAME $A_USERNAME $A_USERNAME $A_USERNAME $A_USERNAME $A_USERNAME $B_USERNAME $B_USERNAME]
set sshPasswordArray {
   "placeholder0"
   "placeholder1"
   "placeholder2"
   "placeholder3"
   "placeholder4"
   "placeholder5"
   "placeholder6"
   "placeholder7"
   "placeholder8"
   "placeholder9"
   "placeholder10"
   "placeholder11"
}

然后,迭代

1
2
3
4
5
6
7
8
for { set index 0 } { $index < [llength $userArray] } { incr index } {
    puts"In loop: $index"
    spawn ssh -o StrictHostKeyChecking=no -l [lindex $userArray $index] [lindex $hostArray $index]
    expect"*?assword:"
    send"[lindex $sshPasswordArray $index]\
"

   #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}

或者,使用 foreach

更具可读性

1
2
3
4
5
6
7
foreach  user $userArray  host $hostArray  sshPassword $sshPasswordArray {
    spawn ssh -o StrictHostKeyChecking=no -l $user $host
    expect"*?assword:"
    send"$sshPassword\
"

   #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}