ssh 跳出 bash 中的 while 循环

发布于 2025-01-08 07:54:16 字数 1063 浏览 1 评论 0原文

我使用这个 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 向标准输出写入一些内容有关,这会混淆“读取”命令。注释掉 ssh 命令可以使循环正常工作。

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

I use this bash-code to upload files to a remote server, for normal files this works fine:

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

The only problem is that for files with a space in the name, the for-loop fails, so I replaced the first line like this:

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

For some strange reason, the ssh-command breaks out of the while-loop, therefore the first missing directory is created fine, but all subsequent missing files/directories are ignored.

I guess this has something to do with ssh writing something to stdout which confuses the "read" command. Commenting out the ssh-command makes the loop work as it should.

Does anybody know why this happens and how one can prevent ssh from breaking the while-loop?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

祁梦 2025-01-15 07:54:17

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

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

您还可以使用 ssh -n 而不是重定向。

The problem is that ssh reads from standard input, therefore it eats all your remaining lines. You can just connect its standard input to nowhere:

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

You can also use ssh -n instead of the redirection.

冰雪之触 2025-01-15 07:54:17

另一种方法是循环除 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< 运算符在这里至关重要,使用 FD 3 而不是默认的 FD 0 (stdin) 。

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

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

Another approach is to loop over a FD other than stdin:

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)

The -u 3 and 3< operators are critical here, using FD 3 rather than the default FD 0 (stdin).

The approach given here -- using -print0, a cleared IFS value, and the like -- is also less buggy than the original code and the existing answer, which can't handle interesting filenames correctly. (Glenn Jackman's answer is close, but even that can't deal with filenames with newlines or filenames with trailing whitespace).

The use of printf %q is critical to generate commands which can't be used to attack the remote machine. Consider what would happen with a file named devel/$(rm -rf /)/hello with code which didn't have this paranoia.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文