Skip to content

Instantly share code, notes, and snippets.

@kkotaro0111
Last active August 19, 2025 05:24
Show Gist options
  • Save kkotaro0111/d991041ceafef7110099260f38f364e3 to your computer and use it in GitHub Desktop.
Save kkotaro0111/d991041ceafef7110099260f38f364e3 to your computer and use it in GitHub Desktop.
cwebpで指定したフォルダ内の画像を再圧縮する( 非可逆 / ほぼロスレス / ロスレス モードはオプションで切り替え)
#!/usr/bin/env bash
# エラーが発生した場合、およびパイプラインでエラーが発生した場合にスクリプトを終了する
set -e
set -o pipefail
# --- ヘルプメッセージ ---
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] <directory> <quality>
Converts all JPG/PNG files in a directory to WebP format.
Original files are moved to a subdirectory named '_original'.
Arguments:
<directory> The target directory containing JPG/PNG files.
<quality> The WebP quality level (0-100) or compression level (0-9) for lossless,
or near-lossless value (0-99) for near-lossless mode.
Options:
-n, --dry-run Show what would be done, without actually modifying any files.
-l, --lossless Use lossless compression (quality = compression level 0-9).
-nl, --near-lossless Use near-lossless mode (quality = preprocessing 0-99).
-h, --help Display this help message and exit.
Modes:
Default: Lossy compression with quality 0-100
--lossless: Lossless compression with level 0-9 (0=fast, 9=best)
--near-lossless: Near-lossless with preprocessing 0-99 (0=best quality)
EOF
exit 1
}
# --- 初期値 ---
DRY_RUN=0
LOSSLESS=0
NEAR_LOSSLESS=0
# --- オプション解析 ---
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--dry-run)
DRY_RUN=1
shift
;;
-l|--lossless)
LOSSLESS=1
shift
;;
-nl|--near-lossless)
NEAR_LOSSLESS=1
shift
;;
-h|--help)
usage
;;
--)
shift
break
;;
-*)
echo "Unknown option: $1"
usage
;;
*)
break
;;
esac
done
# --- 引数のチェック ---
if [ "$#" -ne 2 ]; then
echo "Error: Missing directory or quality argument."
usage
fi
TARGET_DIR=$1
QUALITY=$2
CONVERT_DIR="$(dirname "$TARGET_DIR")/$(basename "$TARGET_DIR")_compressed"
# --- 依存関係のチェック ---
for cmd in cwebp find mv mkdir basename; do
if ! command -v "$cmd" &> /dev/null; then
echo "Error: Required command '$cmd' is not available in your PATH."
if [ "$cmd" = "cwebp" ]; then
echo "Please install WebP tools:"
echo " Ubuntu/Debian: sudo apt install webp"
echo " CentOS/RHEL: sudo yum install libwebp-tools"
echo " macOS: brew install webp"
echo " Arch Linux: sudo pacman -S libwebp"
fi
exit 1
fi
done
# --- 入力値の検証 ---
if [ ! -d "$TARGET_DIR" ]; then
echo "Error: Directory '$TARGET_DIR' not found."
exit 1
fi
# モードの排他チェック
mode_count=0
if [ "$LOSSLESS" -eq 1 ]; then mode_count=$((mode_count + 1)); fi
if [ "$NEAR_LOSSLESS" -eq 1 ]; then mode_count=$((mode_count + 1)); fi
if [ "$mode_count" -gt 1 ]; then
echo "Error: Cannot use multiple compression modes simultaneously."
exit 1
fi
# 品質値の検証
if [ "$LOSSLESS" -eq 1 ]; then
if ! [[ "$QUALITY" =~ ^[0-9]+$ ]] || [ "$QUALITY" -lt 0 ] || [ "$QUALITY" -gt 9 ]; then
echo "Error: Lossless compression level must be between 0 and 9."
exit 1
fi
elif [ "$NEAR_LOSSLESS" -eq 1 ]; then
if ! [[ "$QUALITY" =~ ^[0-9]+$ ]] || [ "$QUALITY" -lt 0 ] || [ "$QUALITY" -gt 99 ]; then
echo "Error: Near-lossless value must be between 0 and 99."
exit 1
fi
else
if ! [[ "$QUALITY" =~ ^[0-9]+$ ]] || [ "$QUALITY" -lt 0 ] || [ "$QUALITY" -gt 100 ]; then
echo "Error: Quality must be a number between 0 and 100."
exit 1
fi
fi
# --- ドライランの表示 ---
if [ "$DRY_RUN" -eq 1 ]; then
echo "*** This is a DRY RUN. No files will be changed. ***"
echo "----------------------------------------------------"
fi
echo "Target directory: $TARGET_DIR"
if [ "$LOSSLESS" -eq 1 ]; then
echo "Compression mode: Lossless (level: $QUALITY)"
elif [ "$NEAR_LOSSLESS" -eq 1 ]; then
echo "Compression mode: Near-lossless (value: $QUALITY)"
else
echo "Compression mode: Lossy (quality: $QUALITY)"
fi
echo "Convert directory: $CONVERT_DIR"
echo ""
# --- 処理の実行 ---
if [ "$DRY_RUN" -eq 0 ] ; then
mkdir -p "$CONVERT_DIR"
fi
processed_files=0
# JPG/JPEG/PNG ファイルを検索
mapfile -t -d '' files < <(find "$TARGET_DIR" -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) -print0)
for file in "${files[@]}"; do
filename=$(basename "$file")
# 拡張子を取得して webp に変更
base_name="${filename%.*}"
relative_path="${file#$TARGET_DIR/}"
relative_dir=$(dirname "$relative_path")
# 作業ファイルのパス
convert_file="${CONVERT_DIR}/${relative_path}"
working_dir=$(dirname "$convert_file")
# WebP ファイルのパス
webp_file="${working_dir}/${base_name}.webp"
if [ "$DRY_RUN" -eq 1 ]; then
if [ "$LOSSLESS" -eq 1 ]; then
echo "[DRY RUN] Would convert '$file' to '$webp_file' (lossless level: $QUALITY)"
elif [ "$NEAR_LOSSLESS" -eq 1 ]; then
echo "[DRY RUN] Would convert '$file' to '$webp_file' (near-lossless: $QUALITY)"
else
echo "[DRY RUN] Would convert '$file' to '$webp_file' (quality: $QUALITY)"
fi
echo "[DRY RUN] Would move original to '$convert_file'"
else
echo "Processing: $relative_path"
# WebP に変換
if [ "$LOSSLESS" -eq 1 ]; then
convert_cmd="cwebp -lossless -z $QUALITY"
mode_desc="lossless level $QUALITY"
elif [ "$NEAR_LOSSLESS" -eq 1 ]; then
convert_cmd="cwebp -near_lossless $QUALITY -z 9"
mode_desc="near-lossless $QUALITY"
else
convert_cmd="cwebp -q $QUALITY"
mode_desc="quality $QUALITY"
fi
# 作業ディレクトリを作成
if ! mkdir -p "$working_dir"; then
echo "Error: Failed to create working directory '$working_dir'"
continue
fi
if $convert_cmd "$file" -o "$webp_file" &>/dev/null; then
echo "✓ Successfully converted: $relative_path → ${base_name}.webp ($mode_desc)"
else
echo "Error: Failed to convert '$file' to '$webp_file'"
continue
fi
fi
processed_files=$((processed_files + 1))
done
echo "----------------------------------------------------"
if [ "$processed_files" -eq 0 ]; then
echo "No JPG/PNG files found to process."
else
if [ "$DRY_RUN" -eq 1 ]; then
echo "Dry run complete. Found $processed_files image file(s)."
else
echo "Successfully converted $processed_files image file(s) to WebP format."
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment