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

Bash并发执行脚本时文件写入异常问题求助

解决Bash并发写入CSV导致内容混乱的问题

你的问题核心是多个并发进程同时写入同一个文件时的竞争条件——多个进程的输出会互相交错覆盖,直接破坏了CSV的行结构。下面给你几个可行的解决思路和具体实现:

方案1:临时文件+最终合并(最稳妥)

让每个并发进程先把结果写入独立的临时文件,等所有进程都执行完毕后,再把所有临时文件的内容合并到最终CSV里。这种方式完全避免了并发写冲突,是最安全的处理方式。

修改后的代码:

#!/bin/bash
function print_not_semver_line() {
    # 为每个进程生成唯一临时文件
    local temp_file=$(mktemp /tmp/repo_semver.XXXXXX)
    echo -n "$repo_name," >> "$temp_file"
    git tag -l | while read -r tag_name;do
        semver "$tag_name" > /dev/null || echo -n "$tag_name " >> "$temp_file"
    done
    echo "" >> "$temp_file"
    # 输出临时文件名,方便后续统一收集
    echo "$temp_file"
}

csv_name=~/Scripts/all_repos/not_semver.csv
echo "Repo Name,Not Semver Versions" > "$csv_name"
# 存储所有临时文件名的数组
temp_files=()

while read -r repo_name;do
    cd "$repo_dir"
    # 后台执行函数,捕获返回的临时文件名
    temp_file=$(print_not_semver_line &)
    temp_files+=("$temp_file")
done < ~/Scripts/all_repos/all_repos.txt

# 等待所有后台进程执行完成
wait

# 合并所有临时文件到最终CSV
cat "${temp_files[@]}" >> "$csv_name"

# 清理临时文件,避免磁盘占用
rm -f "${temp_files[@]}"

方案2:使用文件锁控制写入(直接管控并发写)

利用flock命令为输出文件加排他锁,确保同一时间只有一个进程能写入文件。这种方法不需要临时文件,但要注意尽量缩短持有锁的时间,避免影响并发效率。

修改后的代码:

#!/bin/bash
function print_not_semver_line() {
    # 先把当前进程的结果缓存到变量里,减少持有锁的时间
    local line_content="$repo_name,"
    git tag -l | while read -r tag_name;do
        semver "$tag_name" > /dev/null || line_content+="$tag_name "
    done
    line_content+=$'\n'

    # 用FD 9关联锁文件,避免和其他文件描述符冲突
    {
        echo -n "$line_content"
    } 9>> "$csv_name"
    flock -x 9
}

csv_name=~/Scripts/all_repos/not_semver.csv
echo "Repo Name,Not Semver Versions" > "$csv_name"

while read -r repo_name;do
    cd "$repo_dir"
    print_not_semver_line &
done < ~/Scripts/all_repos/all_repos.txt

# 等待所有后台进程完成
wait

方案3:用GNU Parallel简化并发管理

如果你的系统安装了parallel(大部分Linux发行版可通过包管理器安装),它可以更优雅地处理批量并发任务,默认会保证输出不会交错,还能自动适配CPU核心数调整并发量。

示例代码:

#!/bin/bash
csv_name=~/Scripts/all_repos/not_semver.csv
echo "Repo Name,Not Semver Versions" > "$csv_name"

# 定义处理单个仓库的函数
process_repo() {
    local repo_name="$1"
    cd "$repo_dir"
    local line="$repo_name,"
    git tag -l | while read -r tag_name;do
        semver "$tag_name" > /dev/null || line+="$tag_name "
    done
    echo "$line"
}

# 导出函数和环境变量,让parallel能调用
export -f process_repo
export repo_dir
# 并发执行任务,结果直接追加到CSV
parallel process_repo {} < ~/Scripts/all_repos/all_repos.txt >> "$csv_name"

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 21:52:55