ssh在bash中打破while循环[重复]

Posted

技术标签:

【中文标题】ssh在bash中打破while循环[重复]【英文标题】:ssh breaks out of while-loop in bash [duplicate] 【发布时间】:2016-08-06 15:18:57 【问题描述】:

我使用此 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 33&lt; 运算符在这里很关键,使用 FD 3 而不是默认的 FD 0 (stdin)。

此处给出的方法——使用-print0、清除的IFS 值等——也比原始代码和现有答案更少错误,后者无法正确处理有趣的文件名。 (格伦杰克曼的回答很接近,但即使这样也不能处理带有换行符的文件名或带有尾随空格的文件名)。

printf %q 的使用对于生成不能用于攻击远程计算机的命令至关重要。考虑一个名为devel/$(rm -rf /)/hello 的文件的代码没有这种偏执狂会发生什么。

【讨论】:

以上是关于ssh在bash中打破while循环[重复]的主要内容,如果未能解决你的问题,请参考以下文章

在bash'while read'循环中设置的变量在它之后未设置[重复]

bash脚本中的ssh退出循环[重复]

While循环重置Bash脚本中的数字变量[重复]

打破嵌套循环和主循环[重复]

如何在python的内部for循环中打破while循环?

如何在Python中打破while循环?