我编写了一个 Bash 脚本,该脚本登录到多个远程主机并在 array 中定义的磁盘上运行smartctl
(从包中) 。我能够将数组传递到远程主机,但脚本只是一遍又一遍地回显第一个元素。看起来它将数组视为字符串,并忽略第一个元素之后的所有元素。smartmontools
DISKS
DISKS=("/dev/sda" "/dev/sdb" "/dev/sdc")
HOSTS=("Ariadne" "Nyx")
for i in "${HOSTS[@]}"; do
ssh "$i" "bash" << EOF
for j in "${DISKS[@]}"; do
echo "$j"
done
EOF
done
我尝试在远程主机上定义数组(在第二个 for 循环之前): DISKS=("${DISKS[@]}")
,但这没有帮助。
如何通过 SSH 将数组正确传递到远程主机,然后迭代该数组?
答案1
您可以像这样将数组的定义传递给远程bash
(这里假设远程用户的登录 shell 是bash
):
DISKS_definition=$(typeset -p DISKS)
for i in "${HOSTS[@]}"; do
ssh "$i" "$DISKS_definition"'
for j in "${DISKS[@]}"; do
echo "$j"
done'
done
或者如果使用此处文档方法:
DISKS_definition=$(typeset -p DISKS)
for i in "${HOSTS[@]}"; do
ssh "$i" bash <<EOF
$DISKS_definition
for j in "\${DISKS[@]}"; do
echo "\$j"
done
EOF
done
如果您的ssh
客户端和服务器允许传递LC_*
变量,您也可以这样做
DISKS_definition=$(typeset -p DISKS)
for i in "${HOSTS[@]}"; do
LC_DISKS=$DISKS_definition ssh "$i" bash <<'EOF'
eval "$LC_DISKS"
for j in "${DISKS[@]}"; do
echo "$j"
done
EOF
done
所有内容都在引用中,确定本地或远程 shell 中的哪一个执行扩展。请记住,变量在双引号和此处文档中展开,其中分隔符未加引号(请注意,<<'EOF'
最后一个示例中的 阻止此处文档内的展开,而在前面的示例中(使用<<EOF
),我们必须使用反斜杠来表示我们不需要的内容不想被本地 shell 扩展)。
对于包含非 ASCII 数据的数组,您需要确保本地和远程计算机上区域设置的字符集相同,或者至少避免使用 UTF-8 以外的多字节字符集。
bash-4.3$ locale charmap
ISO-8859-1
bash-4.3$ a=($'foo\xa3$(uname>&2)bar' baz)
bash-4.3$ a_definition=$(typeset -p a)
bash-4.3$ LC_ALL=zh_HK.big5hkscs bash -c "$a_definition"
Linux
通过切换到不同的字符集, 的内容$a_definition
具有不同的含义(此处导致uname>&2
命令被执行)。
答案2
Your是在评估命令echo "$j"
行时评估的,因此在for 循环内它实际上是一个常量(并且未定义)而不是变量。ssh
for j...
尝试这个
for i in "${HOSTS[@]}"; do
ssh "$i" "bash" << EOF
for j in "${DISKS[@]}"; do
echo "\$j"
done
EOF
done
或者,如果您的$DISKS
数组包含没有空格的安全词。
for i in "${HOSTS[@]}"; do
ssh "$i" "for j in ${DISKS[@]}; do echo \"\$j\"; done"
done
我解决此类问题的首选方法是创建一个执行必要工作的脚本并将其驻留在每个远程服务器上。然后,在每个服务器上调用脚本就变成了一个简单明了的过程:
for h in "${HOSTS[@]}"; do ssh "$h" /usr/local/bin/myscript; done
如果您担心脚本会过时,那么在运行它之前从本地系统复制文件并不是不可能的任务。像这样的东西可能会起作用:
for h in "${HOSTS[@]}"
do
scp -p /local/path/to/myscript "$h":myscript.$$
ssh "$h" "myscript.$$; rm -f myscript.$$"
done