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
相关产品推荐
相关产品推荐

