> file.log 追加,结果出现内容错位、半截行、..."/>

Linux 脚本并发执行引发的问题

3次阅读

并发写日志错位因bash重定向非原子,应使用flock、分文件或syslog;后台进程需显式wait防丢失;管道中变量修改不回传,应避免无意义管道。

Linux 脚本并发执行引发的问题

bash & 并发导致文件写入混乱

多个子进程同时往同一个文件用 echo "log" >> file.log 追加,结果出现内容错位、半截行、甚至字节级覆盖——这不是概率问题,是 POSIX shell 重定向的固有行为。系统调用 open(..., O_APPEND) 保证每次 write() 原子追加,但 >> 在 bash 里先 openlseek 到末尾再 write,中间有竞态窗口。

实操建议:

  • flock 包裹写操作:flock /tmp/log.lock -c 'echo "$(date): $MSG" >> /var/log/myapp.log'
  • 避免所有并发进程共用一个日志文件;改用按 PID 或时间戳分文件,如 /tmp/log.$$.txt
  • 若必须集中日志,交给 sysloglogger:它底层用 unix domain socket,天然串行化

wait 丢失导致父脚本提前退出

写了 cmd1 & cmd2 &,但没跟 wait,脚本执行完就退出,后台进程变成孤儿、被 init 收养,后续无法捕获退出码或信号。

实操建议:

  • 显式 wait 是必须的,不是可选优化:cmd1 & p1=$!; cmd2 & p2=$!; wait $p1 $p2
  • 需要等全部后台任务?直接 wait 不带参数即可,它会等当前 shell 启动的所有子进程
  • 注意 set -e 下,某个后台命令失败不会触发退出——wait 才是检查点,务必在 wait 后判断 $?

变量在子 shell 中修改不回传

for i in {1..3}; do echo $i; done | while read n; do count=$((count+1)); done ——最后 $count 还是空。管道每个段都运行在独立子 shell,变量修改只在子 shell 生效。

实操建议:

  • 避免无意义的管道:改用 while read n; do ...; done
  • 真要并行处理数据流?用命名管道(mkfifo)或临时文件中转,别依赖变量跨进程传递
  • 如果只是计数,优先用 wc -l 这类外部命令,而不是在 shell 循环里累加

并发数失控拖垮系统

写了个 for f in *.log; do process_log "$f" & done,目录有 5000 个文件,瞬间拉起 5000 个进程,内存爆满、IO 阻塞、调度失衡。

实操建议:

  • parallel 控制并发度:ls *.log | parallel -j4 process_log-j4 表示最多 4 个并发
  • 不用 parallel?手写信号量:用 mkdir 的原子性模拟锁,配合 while [ $(ls /tmp/sem.* | wc -l) -ge 4 ]; do sleep 0.1; done
  • 注意 ulimit -u 限制用户进程数,别让脚本突破系统级防护

真正麻烦的不是“怎么并发”,而是“怎么安全地收住并发”。资源边界、状态可见性、错误传播路径——这些在单进程里不显眼,一并发就全暴露出来。

text=ZqhQzanResources