Shell脚本并行处理文件异常:部分文件未执行的问题排查与修复
批量Docker并行处理文件脚本异常排查与修复
问题场景
需要批量处理指定文件夹内所有文件,限制最多15个Docker实例并行运行。编写脚本后出现异常:前15个文件正常处理,但后续仅能处理部分剩余文件(比如27个文件只处理了15+6个)。
原脚本代码:
# Path to the folder containing the files INPUT_FILES_FOLDER="/mnt/data/INPUT" OUTPUT_FILES_FOLDER="/mnt/data/OUTPUT" # Path to the Docker image DOCKER_IMAGE="your_docker_image" # Number of parallel instances of Docker to run MAX_PARALLEL=15 # Counter for the number of parallel instances CURRENT_PARALLEL=0 # Function to process files process_files() { for file in "$INPUT_FILES_FOLDER"/*; do input_file=`basename $file` output_file="PROCESSED_${input_file}" input_folder_file="/data/INPUT/${input_file}" output_folder_file="/data/OUTPUT/${output_file}" echo "Input File: $input_file" echo "Output File: $output_file" echo "Input Folder + File: $input_folder_file" echo "Output Folder + File: $output_folder_file" # Check if the current number of parallel instances is less than the maximum allowed if [ "$CURRENT_PARALLEL" -lt "$MAX_PARALLEL" ]; then # Increment the counter for the number of parallel instances ((CURRENT_PARALLEL++)) # Run Docker container in the background, passing the file as input # docker run hello-world docker run --rm -v /mnt/data/:/data my-docker-image:v5.1.0 -i $input_folder_file -o $output_folder_file & # Print a message indicating the file is being processed # echo "Processing $file" else # If the maximum number of parallel instances is reached, wait for one to finish wait -n && ((CURRENT_PARALLEL--)) fi done # Wait for all remaining Docker instances to finish wait } # Call the function to process files process_files
问题根源
核心问题是循环逻辑错误:当CURRENT_PARALLEL达到最大值时,脚本进入else分支执行wait -n && ((CURRENT_PARALLEL--)),但执行完这行后,当前循环迭代的文件并没有被处理——else分支里没有启动Docker容器的代码,这个文件直接被跳过了。
此外还有两个次要问题:
- 没有过滤非文件(如子目录),如果INPUT文件夹下有目录,会导致无效的Docker调用
- 使用反引号和未加引号的变量,存在文件名含空格时的崩溃风险
修正方案
方案一:修复原脚本逻辑
调整循环内的判断顺序,确保每个文件都会进入处理流程:
# Path to the folder containing the files INPUT_FILES_FOLDER="/mnt/data/INPUT" OUTPUT_FILES_FOLDER="/mnt/data/OUTPUT" # Number of parallel instances of Docker to run MAX_PARALLEL=15 # Counter for the number of parallel instances CURRENT_PARALLEL=0 # Function to process files process_files() { for file in "$INPUT_FILES_FOLDER"/*; do # 跳过非文件(比如子目录) [ -f "$file" ] || continue input_file=$(basename "$file") output_file="PROCESSED_${input_file}" input_folder_file="/data/INPUT/${input_file}" output_folder_file="/data/OUTPUT/${output_file}" echo "准备处理文件: $input_file" # 若并行数已满,等待任意一个进程结束再释放槽位 if [ "$CURRENT_PARALLEL" -ge "$MAX_PARALLEL" ]; then wait -n ((CURRENT_PARALLEL--)) fi # 启动当前文件的Docker处理进程 ((CURRENT_PARALLEL++)) docker run --rm -v /mnt/data/:/data my-docker-image:v5.1.0 -i "$input_folder_file" -o "$output_folder_file" & echo "已启动进程,当前并行数: $CURRENT_PARALLEL" done # 等待所有剩余Docker实例完成 wait echo "所有文件处理完成" } # 调用处理函数 process_files
修复要点:
- 先检查并行数,满则等待释放槽位,之后再启动当前文件的处理,确保每个文件都不会被跳过
- 增加非文件过滤逻辑,避免无效调用
- 使用
$(basename "$file")替代反引号,给变量加引号,兼容含空格的文件名
方案二:使用xargs简化并行逻辑
xargs原生支持并行数控制,逻辑更简洁可靠,无需手动维护计数器:
INPUT_FILES_FOLDER="/mnt/data/INPUT" OUTPUT_FILES_FOLDER="/mnt/data/OUTPUT" MAX_PARALLEL=15 # 定义单个文件的处理函数 process_single_file() { file="$1" input_file=$(basename "$file") output_file="PROCESSED_${input_file}" input_path="/data/INPUT/${input_file}" output_path="/data/OUTPUT/${output_file}" echo "处理文件: $input_file" docker run --rm -v /mnt/data/:/data my-docker-image:v5.1.0 -i "$input_path" -o "$output_path" } # 导出函数,让xargs可以调用 export -f process_single_file # 遍历INPUT目录下的所有文件,用xargs并行处理 find "$INPUT_FILES_FOLDER" -maxdepth 1 -type f | xargs -I {} -P "$MAX_PARALLEL" bash -c 'process_single_file "$@"' _ {}
优势:
xargs -P直接控制并行数,无需手动维护计数器,逻辑不易出错find确保只处理文件,自动跳过目录- 自动等待所有进程完成,无需额外
wait命令
内容的提问来源于stack exchange,提问作者newbie
相关产品推荐
相关产品推荐

