ssh在bash中打破while循环[重复]
Posted
技术标签:
【中文标题】ssh在bash中打破while循环[重复]【英文标题】:ssh breaks out of while-loop in bash [duplicate] 【发布时间】:2012-03-12 16:15:02 【问题描述】:我使用此 bash 代码将文件上传到远程服务器,对于普通文件,这可以正常工作:
for i in `find devel/ -newer $UPLOAD_FILE`
do
echo "Upload:" $i
if [ -d $i ]
then
echo "Creating directory" $i
ssh $USER@$SERVER "cd $REMOTE_PATH; mkdir -p $i"
continue
fi
if scp -Cp $i $USER@$SERVER:$REMOTE_PATH/$i
then
echo "$i OK"
else
echo "$i NOK"
rm $UPLOAD_FILE_tmp
fi
done
唯一的问题是对于名称中有空格的文件,for循环失败,所以我将第一行替换为这样:
find devel/ -newer $UPLOAD_FILE | while read i
do
echo "Upload:" $i
if [ -d $i ]
then
echo "Creating directory" $i
ssh $USER@$SERVER "cd $REMOTE_PATH; mkdir -p $i"
continue
fi
if scp -Cp $i $USER@$SERVER:$REMOTE_PATH/$i
then
echo "$i OK"
else
echo "$i NOK"
rm $UPLOAD_FILE_tmp
fi
done
由于某种奇怪的原因,ssh 命令跳出了 while 循环,因此第一个丢失的目录被创建得很好,但所有后续丢失的文件/目录都被忽略了。
我猜这与 ssh 向 stdout 写入内容有关,这会混淆“读取”命令。注释掉 ssh-command 会使循环正常工作。
有谁知道为什么会发生这种情况以及如何防止 ssh 打破 while 循环?
【问题讨论】:
顺便说一句——按照惯例,全大写的变量名是为环境变量和内置变量保留的;它们不应该用于脚本的本地变量。while read
当您的文件名包含文字反斜杠时会严重中断。使用read -r
更安全。同样,read
会从名称中去除尾随空格;为避免这种情况,您需要清除 IFS
。
另外,find | while read
发出一个以换行符分隔的流,但 UNIX 上的合法文件名允许包含换行符。想想如果有人做了mkdir -p devel/$'\n'/etc
然后写信给devel/$'\n'/etc/passwd
会发生什么;此时您最好希望您的脚本无权写入远程计算机上的/etc/passwd
。
此外,如果$i
包含空格或通配符表达式,或者为空,[ -d $i ]
将出现异常。使用[[ -d $i ]]
(如果您的shell 是bash 或其他ksh 衍生工具)或[ -d "$i" ]
,带引号(为了与POSIX 兼容)。
我刚刚发现了这个问题,并且只是想注意,只有当ssh
成功与远程机器建立连接时才会出现错误情况。拒绝连接时不会发生这种情况。
【参考方案1】:
问题是ssh
从标准输入读取,因此它会吃掉所有剩余的行。您可以将其标准输入连接到任何地方:
ssh $USER@$SERVER "cd $REMOTE_PATH; mkdir -p $i" < /dev/null
您也可以使用ssh -n
代替重定向。
【讨论】:
你也可以使用 ssh -n 来完成同样的事情。 顺便说一句,这是相当有问题的。如果$1
是带空格的文件名怎么办?您最终会创建多个目录。 $REMOTE_PATH
也是如此。 printf %q
可用于引用变量以安全地通过 ssh 调用的额外 shell。
谢谢。 :D 在我注意到问题是使用 ssh 而不是脚本的另一部分执行命令之前,我进行了一些调试。
您能否详细说明 ssh 从标准输入读取是什么意思?我试过 "ssh $USER@$SERVER "some commands"; echo "blabla"; 效果很好,所以我不明白它到底是什么意思?
救生员...这让我难过了好几个小时...【参考方案2】:
另一种方法是循环访问除 stdin 之外的 FD:
while IFS= read -u 3 -r -d '' filename; do
if [[ -d $filename ]]; then
printf -v cmd_str 'cd %q; mkdir -p %q' "$REMOTE_PATH" "$filename"
ssh "$USER@$SERVER" "$cmd_str"
else
printf -v remote_path_str '%q@%q:%q/%q' "$USER" "$SERVER" "$REMOTE_PATH" "$filename"
scp -Cp "$filename" "$remote_path_str"
fi
done 3< <(find devel/ -newer "$UPLOAD_FILE" -print0)
-u 3
和 3<
运算符在这里很关键,使用 FD 3 而不是默认的 FD 0 (stdin)。
此处给出的方法——使用-print0
、清除的IFS
值等——也比原始代码和现有答案更少错误,后者无法正确处理有趣的文件名。 (格伦杰克曼的回答很接近,但即使这样也不能处理带有换行符的文件名或带有尾随空格的文件名)。
printf %q
的使用对于生成不能用于攻击远程计算机的命令至关重要。考虑一个名为devel/$(rm -rf /)/hello
的文件的代码没有这种偏执狂会发生什么。
【讨论】:
以上是关于ssh在bash中打破while循环[重复]的主要内容,如果未能解决你的问题,请参考以下文章