shell脚本中while循环只执行1次的原因?

#! /bin/bash

RemoteUser=root

RemoteIP=192.168.XXX.XXX

# 远程备份服务器存放路径
RemoteBackupDir=/home/backups

#日志输出目录
BackupLog=/var/opt/logs/backup.log

if [ $? -eq 0 ]; then

    # 获取远程备份列表
    ssh "$RemoteUser@$RemoteIP" "find '$RemoteBackupDir' -type f -name '*.tar' -exec stat -c '%Y %n' {} \; | sort -n" > $RemoteBackupList
    
    SevenDaysAgo=$(date -d '7 days ago' +%s)
      
    # 删除超过7天的备份
    while IFS= read -r line || [[ -n $line ]]; do
    
        echo "进入循环>>>>" >> $BackupLog
    
        modtime_seconds=$(echo "$line" | awk '{print $1}')            
        filepath=$(echo "$line" | awk '{print $2}')
        
        if (( modtime_seconds < SevenDaysAgo )); then
            # 注释ssh语句 进入循环就能打印多次, 放开就只打印一次
            ssh "$RemoteUser@$RemoteIP" "rm -f '$filepath'" >> $BackupLog 2>&1      
            
            if [ $? -ne 0 ]; then
                echo "删除远程旧备份失败: $filepath" >> $BackupLog                      
            fi
        fi
    done < "$RemoteBackupList"
      
    # 清理临时文件
    rm -f "$RemoteBackupList"
else
    echo ">>>>>远程备份失败>>>>>>$(date +"%Y-%m-%d %H:%M:%S")" >> $BackupLog
fi

问题是:只要注释ssh "$RemoteUser@$RemoteIP" "rm -f '$filepath'" >> $BackupLog 2>&1这条语句,echo "进入循环>>>>" >> $BackupLog 就能多次打印, 一旦放开就只执行一次,也没有任何报错; 现象就是只删除了1个符合条件的文件,其余的都没删掉;
$RemoteBackupList也没有啥问题,格式就是下面的

1726239998 /home/1726066986_2024_09_11_14.6.1_test_backup.tar
1728871975 /home/1728831723_2024_10_13_14.6.1_test_backup.tar
1728885726 /home/1728871651_2024_10_14_14.6.1_test_backup.tar
1728894007 /home/1728885455_2024_10_14_14.6.1_test_backup.tar
1728895145 /home/1728894838_2024_10_14_14.6.1_test_backup.tar
1728895857 /home/1728895569_2024_10_14_14.6.1_test_backup.tar
1728899038 /home/1728898757_2024_10_14_14.6.1_test_backup.tar
...省略号代替...

虽然最后改用了find xxx -exec,没用while,但还是想知道while这里有什么问题

阅读 839
avatarAI BotBETA

在你的脚本中,while 循环只执行一次的问题很可能是由于 ssh 命令的执行结果(尤其是标准错误输出)影响了脚本的继续执行。具体来说,当 ssh 命令尝试删除文件但遇到权限问题、网络问题或其他任何导致命令失败的情况时,它可能会输出错误信息到标准错误(stderr),而这些错误信息没有被适当地处理或忽略。

在你的脚本中,ssh 命令的输出被重定向到 $BackupLog(包括标准输出和标准错误),但这并不直接影响循环的迭代。然而,如果 ssh 命令失败并且其退出状态($?)影响了脚本中其他部分的逻辑(尽管在你的 while 循环内部没有直接检查 $?),或者 ssh 命令的输出(特别是错误输出)以某种方式干扰了输入流(尽管在你的脚本中这不太可能,因为输入是通过重定向文件 $RemoteBackupList 提供的),那么可能会出现问题。

但更常见的情况是,如果 ssh 命令(或其调用的 rm 命令)因为某些原因(如权限不足)而失败,并且这个错误没有被捕获或适当处理,它可能会影响脚本的后续执行,尽管这通常不会直接导致 while 循环只迭代一次。

然而,考虑到你描述的行为,这里有几个可能的原因和解决方案:

  1. 检查 ssh 命令的退出状态
    确保 ssh 命令的退出状态被正确处理。虽然你在 ssh 命令之后检查了 $?,但这是在循环的外部进行的,并且对于循环内的每次迭代都不适用。你应该在循环内部检查 ssh 命令的退出状态,并相应地处理错误。
  2. 重定向或忽略 ssh 的标准错误
    如果你不关心 ssh 命令的错误输出,可以将其重定向到 /dev/null。这样,即使命令失败,它也不会干扰脚本的其余部分。

    ssh "$RemoteUser@$RemoteIP" "rm -f '$filepath'" >> $BackupLog 2>/dev/null
  3. 检查 $RemoteBackupList 文件的内容
    确保 $RemoteBackupList 文件在 while 循环之前已经正确生成,并且包含了你期望的所有行。可以在循环之前添加一些调试语句来打印文件内容。
  4. 使用更健壮的错误处理
    while 循环中添加更详细的错误处理逻辑,以便在出现问题时能够更清楚地了解发生了什么。
  5. 考虑使用 xargsfind -exec
    你已经提到了使用 find -exec,这是一个很好的选择,因为它可以直接在远程服务器上执行删除操作,减少了脚本的复杂性和潜在的错误源。

综上所述,最可能的原因是 ssh 命令的失败及其错误处理不足。通过改进错误处理和检查,你应该能够解决这个问题。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏