ssh打破了bash中的while循环

我使用这个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循环会失败,所以我将第一行replace为:

 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-command跳出了while循环,因此第一个丢失的目录被创build好了,但是后来丢失的所有文件/目录都被忽略了。

我想这与ssh写一些标准输出混淆了“读取”命令有关。 注释掉ssh-command会使循环正常工作。

有谁知道为什么会发生这种情况,以及如何防止ssh打破while循环?

问题是, ssh从标准input读取,因此它会吃掉所有剩余的行。 您可以将其标准input连接到任何地方:

 ssh $USER@$SERVER "cd ${REMOTE_PATH}; mkdir -p $i" < /dev/null 

您也可以使用ssh -n而不是redirect。

另一种方法是循环使用除标准input以外的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 33<操作符在这里很重要,使用FD 3而不是默认的FD 0(stdin)。

这里给出的方法 – 使用-print0 ,一个清除的IFS值等 – 也比原始代码和现有答案less,它不能正确处理有趣的文件名。 (Glenn Jackman的回答很接近,但即使这样也无法处理带有换行符或带有空格的文件名的文件名)。

使用printf %q对于生成不能用来攻击远程机器的命令是非常重要的。 考虑一下名为devel/$(rm -rf /)/hello的文件会发生什么情况,代码中没有这个偏执狂。

除了choroba的答案之外 ,不要使用for循环来读取文件名:

 find devel/ -newer $UPLOAD_FILE | while read -ri do ...