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

Bash中for与while循环嵌套实现区块数对比的问题排查

问题排查:Bash脚本实现区块数对比更新失败

让我来帮你排查这两次嵌套尝试的问题,顺便给你一个可行的修复方案~

需求回顾

我需要编写Bash脚本实现以下功能:

  • 遍历/usr/local/bin/下所有*-cli命令,执行getblockcount获取当前区块数
  • 读取blocks.log文件,将当前区块数与文件中对应币种的记录对比:
    • 若文件无该币种记录则添加
    • 若有则在当前区块数更大时更新记录

已有可用代码

遍历获取区块数的for循环

这段代码能正确遍历所有cli命令,提取币种名并输出区块数:

#!/bin/bash
wallets=`find /usr/local/bin/ -iname '*-cli'`
for i in $wallets; do
current_blocks=`$i getblockcount`
coin_name=${i:15:-4} # 截取路径与后缀,如将/usr/local/bin/bitcoin-cli转为bitcoin
echo $coin_name $current_blocks
echo $coin_name $current_blocks >> blocks.log
done

执行输出示例:

bitcoin 1457824
litecoin 759345

读取日志的while循环

这段代码能正确读取blocks.log的每一行:

while read line ; do
set $line
echo $1 $2
done < blocks.log

两次嵌套尝试的错误分析

第一次尝试的问题

for i in $wallets; do
current_blocks=`$i getblockcount`
coin_name=${i:15:-4}
while read line; do
set $line
if [ "$1" == "$coin_name" ]; then
echo "File contains the coin_name, compare the blocks now"
if (( "$current_blocks" >= "$2" )); then
echo "Current blocks are greater than the saved blocks"
echo "Saving the new blocks count now"
sed -i "s/$1/$1 $current_blocks/" blocks.log
else
echo "Current blocks are less than or equals to saved blocks"
fi
else
echo "File does not contain the coin_name, adding it now"
echo "$coin_name $current_blocks" >> blocks.log
fi
done < blocks.log
done

这里有三个核心问题:

  • 重复读取与重复添加:每次外层for循环都会从头读取整个blocks.log,文件里每一条不匹配当前币种的记录都会触发一次else分支,导致同一个币种被多次追加到日志里。
  • sed替换语法错误:s/$1/$1 $current_blocks/会把所有包含币种名的文本替换,比如原行是bitcoin 1457823,替换后会变成bitcoin 1457824 1457823,完全不符合预期。正确的替换应该是匹配整行开头的币种名,替换整行的区块数。
  • 读写文件冲突:在while循环读取blocks.log的同时用sed -i修改它,会导致文件指针混乱,读取的内容可能不是最新的,甚至出现读取异常。

第二次尝试的问题

for i in $wallets; do
current_blocks=`$i getblockcount`
coin_name=${i:15:-4}
read line < blocks.log
set $line
if [ "$1" == "$coin_name" ]; then
echo "File contains the coin_name, compare the blocks now"
if (( "$current_blocks" >= "$2" )); then
echo "Current blocks are greater than the saved blocks"
echo "Saving the new blocks count now"
# sed -i "s/$1/$1 $current_blocks/" blocks.log
else
echo "Current blocks are less than or equals to saved blocks"
fi
else
echo "File does not contain the coin_name, adding it now"
echo "$coin_name $current_blocks" >> blocks.log
fi
done

这里的核心问题是只读取了日志的第一行:read line < blocks.log每次只会读取文件的第一行,所以只有第一个币种能被正确检查,后续的币种即使已经存在于日志中,也会被误判为不存在而重复添加。

修复后的可行方案

我们可以简化逻辑,不需要嵌套循环,而是用grep先检查币种是否存在,再做对应的操作,同时用临时文件避免读写冲突:

#!/bin/bash
LOG_FILE="blocks.log"
# 确保日志文件存在,避免首次运行报错
touch "$LOG_FILE"

# 遍历所有*-cli命令(用通配符更简洁,替代find)
for cli_path in /usr/local/bin/*-cli; do
    # 跳过非文件(比如目录,虽然通配符一般不会返回,但做个保险)
    [ -f "$cli_path" ] || continue

    # 获取当前区块数和币种名(用更健壮的字符串截取,适配不同路径长度)
    current_blocks=$("$cli_path" getblockcount)
    coin_name=${cli_path##*/}  # 提取文件名,比如bitcoin-cli
    coin_name=${coin_name%-cli} # 去掉-cli后缀,得到bitcoin

    # 检查日志中是否存在该币种的记录(^$coin_name 确保匹配整行开头,避免部分匹配)
    if grep -q "^$coin_name " "$LOG_FILE"; then
        # 提取已保存的区块数
        saved_blocks=$(grep "^$coin_name " "$LOG_FILE" | awk '{print $2}')
        # 对比区块数
        if (( current_blocks > saved_blocks )); then
            echo "更新币种 $coin_name:区块数从 $saved_blocks 变为 $current_blocks"
            # 使用临时文件替换,避免直接修改原文件的读写冲突
            sed "s/^$coin_name .*/$coin_name $current_blocks/" "$LOG_FILE" > "$LOG_FILE.tmp"
            mv "$LOG_FILE.tmp" "$LOG_FILE"
        else
            echo "币种 $coin_name:当前区块数 $current_blocks 不大于已保存的 $saved_blocks,无需更新"
        fi
    else
        # 不存在则追加记录
        echo "添加新币种 $coin_name,区块数 $current_blocks"
        echo "$coin_name $current_blocks" >> "$LOG_FILE"
    fi
done

代码说明

  • 更健壮的币种名提取:用${cli_path##*/}和${coin_name%-cli}替代固定长度截取,即使路径长度变化也能正常工作。
  • 精准的存在性检查:grep -q "^$coin_name "确保匹配整行开头的币种名,避免类似bitcoin和bitcoincash的混淆。
  • 安全的文件修改:先用sed生成临时文件,再替换原文件,彻底避免读写原文件时的冲突。

内容的提问来源于stack exchange,提问作者Marry Jane

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:49:36