如何使用 Expect/Tcl 中的数组进行 ssh?

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 一起使用时遇到问题。

#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/\r" }



#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)\r"
    #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}

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

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

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后面加个\r(模拟按Return 键)并且不要忘记 close 在循环结束时生成的子进程。 (这对于 10 个项目通常并不重要,但是您可以同时打开的虚拟终端数量相当少,并且包括您机器上的所有其他用户;最好保持同时打开的数量尽可能小尽可能。)


您也可以在 array set 处使用它,但是如果您的变量中有空格,它可能会松动,所以我真的不推荐它:

array set userArray [subst {
    0 $A_USERNAME
    1 $A_USERNAME
    ... ...
}]

注意:array set 根本不支持评论格式。您在示例中使用键 #11 进行了输入…

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

你也可以这样做:

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"
}

然后,迭代

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]\r"
    #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}

或者,使用 foreach 更具可读性

foreach  user $userArray  host $hostArray  sshPassword $sshPasswordArray {
    spawn ssh -o StrictHostKeyChecking=no -l $user $host
    expect "*?assword:" 
    send "$sshPassword\r"
    #do some stuff here including executing $LINUX_HARDWARE_COLLECTION_FILE...
}