blog
2026-10-05
0 阅读
一个批量转换图片到avif的脚本
脚本:
#!/bin/bash
# ============================================================
# 默认参数
# ============================================================
INPUT_DIR=""
OUTPUT_DIR=""
FORMATS="jpg,jpeg,png,webp,gif,tiff,bmp"
QUALITY=50
PARALLEL=1
LOG_FILE=""
RECURSIVE=0
DRY_RUN=0
MAX_SIZE_MB=200
REPORT_FILE="avif_report.md"
RETRY_FILE="avif_retry.list"
# ============================================================
# 帮助信息
# ============================================================
usage() {
echo "用法: $0 -i 输入目录 [-o 输出目录] [-f 格式列表] [-q 压缩率]"
echo " [--parallel N] [--log file] [--recursive] [--dry-run]"
echo " [--max-size MB] [--report file]"
exit 1
}
# ============================================================
# 参数解析
# ============================================================
while [[ $# -gt 0 ]]; do
case "$1" in
-i) INPUT_DIR="$2"; shift 2 ;;
-o) OUTPUT_DIR="$2"; shift 2 ;;
-f) FORMATS="$2"; shift 2 ;;
-q) QUALITY="$2"; shift 2 ;;
--parallel) PARALLEL="$2"; shift 2 ;;
--log) LOG_FILE="$2"; shift 2 ;;
--recursive) RECURSIVE=1; shift ;;
--dry-run) DRY_RUN=1; shift ;;
--max-size) MAX_SIZE_MB="$2"; shift 2 ;;
--report) REPORT_FILE="$2"; shift 2 ;;
*) usage ;;
esac
done
# ============================================================
# 自动安装依赖
# ============================================================
install_dependencies() {
echo "???? 正在检测依赖..."
if command -v magick >/dev/null 2>&1; then
echo "✔ 已找到 ImageMagick(magick)"
CONVERTER="magick"
return
fi
if command -v avifenc >/dev/null 2>&1; then
echo "✔ 已找到 avifenc"
CONVERTER="avifenc"
return
fi
echo "⚠ 未找到 magick 或 avifenc,开始自动安装 ImageMagick..."
# 检测系统类型
if [[ -f /etc/debian_version ]]; then
echo "???? 检测到 Debian/Ubuntu 系统,正在安装..."
sudo apt update && sudo apt install -y imagemagick
elif [[ -f /etc/redhat-release ]]; then
echo "???? 检测到 CentOS/RHEL 系统,正在安装..."
sudo yum install -y epel-release
sudo yum install -y ImageMagick
elif [[ -f /etc/arch-release ]]; then
echo "???? 检测到 Arch Linux 系统,正在安装..."
sudo pacman -Sy --noconfirm imagemagick
elif [[ "$(uname)" == "Darwin" ]]; then
echo "???? 检测到 macOS,正在安装..."
brew install imagemagick
else
echo "❌ 无法识别系统类型,请手动安装 ImageMagick"
exit 1
fi
# 再次检测
if command -v magick >/dev/null 2>&1; then
echo "✔ ImageMagick 安装成功"
CONVERTER="magick"
else
echo "❌ ImageMagick 安装失败,请手动安装"
exit 1
fi
}
install_dependencies
# ============================================================
# 参数检查
# ============================================================
if [[ -z "$INPUT_DIR" ]]; then
echo "❌ 必须指定输入目录"
usage
fi
if [[ ! -d "$INPUT_DIR" ]]; then
echo "❌ 输入目录不存在: $INPUT_DIR"
exit 1
fi
if [[ -z "$OUTPUT_DIR" ]]; then
OUTPUT_DIR="$INPUT_DIR/avif"
fi
mkdir -p "$OUTPUT_DIR"
echo "???? 输入目录: $INPUT_DIR"
echo "???? 输出目录: $OUTPUT_DIR"
echo "???? 压缩率: $QUALITY"
echo "???? 使用转换工具: $CONVERTER"
# ============================================================
# 日志函数
# ============================================================
log() {
[[ -n "$LOG_FILE" ]] && echo "$(date '+%F %T') $1" >> "$LOG_FILE"
}
# ============================================================
# 查找文件
# ============================================================
IFS=',' read -ra EXT_LIST "$REPORT_FILE"
echo "| 原文件 | 原大小 | AVIF大小 | 压缩率 | 状态 |" >> "$REPORT_FILE"
echo "|--------|--------|----------|--------|--------|" >> "$REPORT_FILE"
# ============================================================
# 转换函数
# ============================================================
convert_one() {
local img="$1"
local filename=$(basename "$img")
local output="$OUTPUT_DIR/${filename%.*}.avif"
if [[ -f "$output" ]]; then
echo "⏭ 跳过已存在: $output"
log "skip $img"
return
fi
local size_mb=$(du -m "$img" | cut -f1)
if (( size_mb > MAX_SIZE_MB )); then
echo "⚠️ 跳过过大文件 ($size_mb MB): $img"
log "too_large $img"
return
fi
if [[ $DRY_RUN -eq 1 ]]; then
echo "???? 将转换: $img → $output"
return
fi
echo "➡️ 转换: $img → $output"
log "convert $img"
local before_size=$(du -h "$img" | cut -f1)
if [[ "$CONVERTER" == "magick" ]]; then
magick "$img" -quality "$QUALITY" "$output"
else
avifenc --min 0 --max "$QUALITY" "$img" "$output"
fi
if [[ $? -ne 0 ]]; then
echo "❌ 转换失败: $img"
log "fail $img"
echo "$img" >> "$RETRY_FILE"
echo "| $filename | $before_size | - | - | ❌失败 |" >> "$REPORT_FILE"
return
fi
local after_size=$(du -h "$output" | cut -f1)
local before_k=$(du -k "$img" | cut -f1)
local after_k=$(du -k "$output" | cut -f1)
local ratio=$(( 100 * after_k / before_k ))
echo "| $filename | $before_size | $after_size | ${ratio}% | ✅成功 |" >> "$REPORT_FILE"
}
export -f convert_one
export OUTPUT_DIR QUALITY CONVERTER LOG_FILE DRY_RUN MAX_SIZE_MB REPORT_FILE RETRY_FILE
# ============================================================
# 并行执行 + 进度条
# ============================================================
count=0
printf "%s\n" "${FILES[@]}" | xargs -I {} -P "$PARALLEL" bash -c 'convert_one "$@"' _ {} | while read -r line; do
count=$((count + 1))
percent=$((count * 100 / TOTAL))
echo -ne "进度: $percent% ($count/$TOTAL)\r"
echo "$line"
done
echo ""
echo "✅ 全部完成"
log "done"
echo "???? 转换报告已生成: $REPORT_FILE"
[[ -f "$RETRY_FILE" ]] && echo "⚠️ 有失败的文件,重试列表: $RETRY_FILE"???? **使用说明(完整文档)**
## 1. 基本用法
bash
```
./convert_to_avif.sh -i ./images
```
## 2. 指定输出目录
bash
```
./convert_to_avif.sh -i ./images -o ./output_avif
```
## 3. 指定格式
bash
```
./convert_to_avif.sh -i ./images -f jpg,png,webp
```
## 4. 指定压缩率
bash
```
./convert_to_avif.sh -i ./images -q 60
```
## 5. 并行转换(提高速度)
bash
```
./convert_to_avif.sh -i ./images --parallel 4
```
## 6. 递归扫描子目录
bash
```
./convert_to_avif.sh -i ./images --recursive
```
## 7. 记录日志
bash
```
./convert_to_avif.sh -i ./images --log convert.log
```
## 8. dry-run(仅查看将转换哪些文件)
bash
```
./convert_to_avif.sh -i ./images --dry-run
```
## 9. 限制最大文件大小(避免处理超大图)
bash
```
./convert_to_avif.sh -i ./images --max-size 100
```
## 10. 指定报告文件名
bash
```
./convert_to_avif.sh -i ./images --report my_report.md