我终于解决了几个星期以来一直困扰我的问题。我使用带有“授权密钥”的 SSH 来远程运行命令。一切都很好,除非我在 while 循环中执行此操作。使用 ssh 命令完成任何迭代后循环终止。
很长一段时间我都认为这是 ksh 的某种怪异之处,但现在我发现 bash 的行为实际上是相同的。
一个重现该问题的小示例程序。这是从一个更大的实现中提炼出来的,该实现拍摄快照并在集群中的节点之间复制它们。
#!/bin/bash
set -x
IDTAG=".*zone"
MARKER="mark-$(date +%Y.%m.%d.%H.%M.%S)"
REMOTE_HOST=sol10-target
ZFSPARENT=rpool
ssh $REMOTE_HOST zfs list -t filesystem -rHo name,mounted $ZFSPARENT | grep "/$IDTAG " > /tmp/actionlist
#for RMT_FILESYSTEM in $(cat /tmp/actionlist)
cat /tmp/actionlist | while read RMT_FILESYSTEM ISMOUNTED
do
echo ${RMT_FILESYSTEM}@${MARKER}
[ "$ISMOUNTED" = "yes" ] && ssh $REMOTE_HOST zfs snapshot -r ${RMT_FILESYSTEM}@${MARKER}
echo Remote Command Return Code: $?
done
(请注意,根据 zfs list“-H”选项的行为定义,grep 搜索表达式中有一个制表符。)
我的示例有一些用于根的 ZFS 文件系统,其中所有“区域”的根文件系统都位于名为类似于的数据集上
POOL/区域/app1zone
POOL/区域/group2/app2zone
ETC。
上面的循环应该为每个选定的数据集创建一个快照,但它只对第一个数据集进行操作,然后退出。
程序找到正确数量的数据集可以通过在脚本存在后检查“/tmp/actionlist”文件来轻松确认。
例如,如果 ssh 命令被替换为 echo 命令,则循环将迭代所有输入行。或者我最喜欢的 - 在有问题的命令前面加上“echo”。
如果我使用 for 循环,那么它也可以工作,但由于数据集列表的潜在大小,这可能会导致最大扩展命令行长度出现问题。
我现在 99.999% 确信只有那些包含 ssh 命令的循环才会给我带来问题!
请注意,ssh 命令运行的迭代已完成!就好像插入 while 循环的数据突然丢失...如果前几行输入不执行 ssh 命令,则循环将继续下去,直到实际运行 SSH 命令。
在我正在测试的笔记本电脑上,我有两个 Solaris 10 VM,只有大约两到三个示例数据集,但在大型 SPARC 系统上也发生了同样的情况,在该系统上,这意味着要上线,并且有很多数据集。
答案1
SSH 可能会从标准输入读取数据,从而耗尽您的操作列表。尝试将 ssh 的标准输入重定向到 /dev/null:
ssh $REMOTE_HOST zfs snapshot -r ${RMT_FILESYSTEM}@${MARKER} </dev/null
作为一般规则,当在 -style 循环下运行可能干扰标准输入的命令时while read
,我喜欢将整个循环体包裹在大括号中:
cat /tmp/uuoc | while read RMT_FILESYSTEM ISMOUNTED
do {
echo ${RMT_FILESYSTEM}@${MARKER}
[ "$ISMOUNTED" = "yes" ] && ssh $REMOTE_HOST zfs snapshot -r ${RMT_FILESYSTEM}@${MARKER}
echo Remote Command Return Code: $?
} < /dev/null; done