You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Bash脚本中While循环仅执行一次的问题排查

问题:Bash循环处理服务器列表仅执行一次就停止

我编写了如下Bash脚本用于批量部署软件包:

#!/bin/bash

USERNAME=xenobot
SSH_KEY_LOCATION="~/.ssh/id_rsa"
FILE="server.info"
while read -r server_ip  package_type; do  
    if [[ $package_type == "deb" ]]; then
         echo "For $server_ip package type is $package_type"
         DPKG_FILE_PATH=$(ls ./src/dpkg/dpkg-*)
             DPKG_FILE=$(basename $DPKG_FILE_PATH)
             echo "$DPKG_FILE located at $DPKG_FILE_PATH will be transfered via SSH to server"
         scp ./src/dpkg/${DPKG_FILE} $USERNAME@$server_ip:/tmp
             ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo dpkg -i *.deb"
    elif [[ $package_type == "rpm" ]]; then
         echo "For $server_ip package type is $package_type"
             RPM_FILE_PATH=$(ls ./src/rpm/rpm-*)
             RPM_FILE=$(basename $RPM_FILE_PATH)
             echo "$RPM_FILE located at $RPM_FILE_PATH will be transfered via SSH to server"
         scp -o 'StrictHostKeyChecking no' ./src/rpm/${RPM_FILE} $USERNAME@$server_ip:/tmp
         echo "$RPM_FILE has been successfuly transfered to server!"
         ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo rpm -ivh --force $RPM_FILE"
    fi
done <"$FILE"

脚本读取server.info文件内容(内容为213.136.80.123 rpm 213.136.91.102 rpm)执行部署,但运行时循环仅处理第一条记录就停止,未执行第二条。

而仅保留打印逻辑的测试脚本:

#!/bin/bash

USERNAME=xenobot
SSH_KEY_LOCATION="~/.ssh/id_rsa"
FILE="server.info"
while read -r server_ip  package_type; do  
    if [[ $package_type == "deb" ]]; then
         echo "For $server_ip package type is $package_type"
    elif [[ $package_type == "rpm" ]]; then
         echo "For $server_ip package type is $package_type"
    fi
done <"$FILE"

可正常输出两条记录,需排查循环仅执行一次的原因。

原因分析

问题出在ssh和scp命令上:这两个命令默认会读取标准输入(stdin),而你的while循环是通过重定向从$FILE读取输入(此时stdin指向$FILE)。当第一次循环中执行ssh或scp时,它们会把$FILE中剩余的所有内容全部读走,导致下一次read命令没有内容可读取,循环直接终止。

解决方案

有两种可靠的解决方式:

方式一:给ssh/scp添加-n参数

-n参数会让ssh/scp不读取标准输入,避免它们消耗$FILE中剩余的内容。修改脚本中的scp和ssh命令:

处理deb包的部分:

scp -n ./src/dpkg/${DPKG_FILE} $USERNAME@$server_ip:/tmp
ssh -n -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo dpkg -i *.deb"

处理rpm包的部分:

scp -n -o 'StrictHostKeyChecking no' ./src/rpm/${RPM_FILE} $USERNAME@$server_ip:/tmp
ssh -n -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -l ${USERNAME} ${server_ip} "cd /tmp; sudo rpm -ivh --force $RPM_FILE"

方式二:使用独立的文件描述符读取输入

将$FILE的输入重定向到一个非标准的文件描述符(比如3),让read从这个描述符读取内容,这样ssh/scp就不会干扰循环的输入源。修改循环的写法:

while read -r server_ip package_type <&3; do  
    # 原有循环内的所有逻辑保持不变
done 3<"$FILE"

这种方式彻底隔离了循环的输入和ssh/scp的输入,避免冲突。

内容的提问来源于stack exchange,提问作者Ruslan Pylypiuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 08:25:20