Skip to content

Instantly share code, notes, and snippets.

@kkotaro0111
Last active August 19, 2025 05:23
Show Gist options
  • Save kkotaro0111/67462356141b3c008f1609d64ab644fd to your computer and use it in GitHub Desktop.
Save kkotaro0111/67462356141b3c008f1609d64ab644fd to your computer and use it in GitHub Desktop.
pngquant / pngopt で指定したフォルダ内のpng画像を再圧縮する
#!/usr/bin/env bash
# エラーが発生した場合、およびパイプラインでエラーが発生した場合にスクリプトを終了する
set -e
set -o pipefail
# --- ヘルプメッセージ ---
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS] <directory> <compression_level>
Re-compresses all PNG files in a directory using pngquant and optipng.
Original files are moved to a subdirectory named '_original'.
Arguments:
<directory> The target directory containing PNG files.
<compression_level> The compression level (1-9, where 9 is maximum compression).
Options:
-n, --dry-run Show what would be done, without actually modifying any files.
-h, --help Display this help message and exit.
EOF
exit 1
}
# --- 初期値 ---
DRY_RUN=0
# --- オプション解析 ---
# getoptsではなく、より柔軟なループを使用
while [[ $# -gt 0 ]]; do
case "$1" in
-n|--dry-run)
DRY_RUN=1
shift # 引数をシフト
;;
-h|--help)
usage
;;
--)
shift # -- の後を引数として扱う
break
;;
-*)
echo "Unknown option: $1"
usage
;;
*)
break # オプション以外の引数が見つかったらループを抜ける
;;
esac
done
# --- 引数のチェック ---
if [ "$#" -ne 2 ]; then
echo "Error: Missing directory or compression level argument."
usage
fi
TARGET_DIR=$1
COMPRESSION_LEVEL=$2
COMPRESSED_DIR="$(dirname "$TARGET_DIR")/$(basename "$TARGET_DIR")_compressed"
# --- 依存関係のチェック ---
for cmd in pngquant optipng find mv mkdir basename; do
if ! command -v "$cmd" &> /dev/null; then
echo "Error: Required command '$cmd' is not available in your PATH."
exit 1
fi
done
# --- 入力値の検証 ---
if [ ! -d "$TARGET_DIR" ]; then
echo "Error: Directory '$TARGET_DIR' not found."
exit 1
fi
if ! [[ "$COMPRESSION_LEVEL" =~ ^[1-9]$ ]]; then
echo "Error: Compression level must be a number between 1 and 9."
exit 1
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"
echo "Compression level: $COMPRESSION_LEVEL"
echo "Compression directory: $COMPRESSED_DIR"
echo ""
# --- 処理の実行 ---
if [ "$DRY_RUN" -eq 0 ]; then
mkdir -p "$COMPRESSED_DIR"
fi
processed_files=0
# findで見つかったファイルを配列に格納してからループ処理
mapfile -t -d '' files < <(find "$TARGET_DIR" -type f -iname "*.png" -print0)
for file in "${files[@]}"; do
filename=$(basename "$file")
# 相対パスを取得してサブディレクトリ構造を保持
relative_path="${file#$TARGET_DIR/}"
convert_file="${COMPRESSED_DIR}/${relative_path}"
working_dir=$(dirname "$convert_file")
if [ "$DRY_RUN" -eq 1 ]; then
echo "[DRY RUN] Would re-compress to '$convert_file' with compression level $COMPRESSION_LEVEL"
else
echo "Processing: $relative_path"
# 1. 作業ディレクトリを作成
if ! mkdir -p "$working_dir"; then
echo "Error: Failed to create working directory '$working_dir'"
continue
fi
# 2. 再圧縮 (pngquant + optipng)
temp_file="${convert_file}.tmp"
if pngquant --quality=65-80 --output "$temp_file" "$file" 2>/dev/null && \
optipng -o"$COMPRESSION_LEVEL" -quiet "$temp_file" 2>/dev/null && \
mv "$temp_file" "$convert_file"; then
echo "✓ Successfully processed: $relative_path"
else
echo "Error: Failed to compress '$relative_path'. Restoring original file."
# 失敗した場合、一時ファイルを削除してオリジナルファイルを復元
rm -f "$temp_file"
continue
fi
fi
processed_files=$((processed_files + 1))
done
echo "----------------------------------------------------"
if [ "$processed_files" -eq 0 ]; then
echo "No PNG files found to process."
else
if [ "$DRY_RUN" -eq 1 ]; then
echo "Dry run complete. Found $processed_files PNG file(s)."
else
echo "Successfully re-compressed $processed_files PNG file(s)."
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment