-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathzip-optimizer.sh
More file actions
executable file
·105 lines (83 loc) · 3.53 KB
/
zip-optimizer.sh
File metadata and controls
executable file
·105 lines (83 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/bin/bash
# (C) 2025 - 2026 Benjamin Steenkamer
# Recompress all the ZIP archives provided using the highest compression level
# Replaces the original ZIP archive if space is saved
# Prints the space saved per file and the total space saved at the end
# Usage: ./zip-optimizer <directory or ZIP> <directory or ZIP> ...
exit_script() {
if [ -n "$TEMP_DIR" ]; then
rm -rf "$TEMP_DIR"
fi
if [ -n "$total_saved_bytes" ]; then
total_saved_human=$(numfmt --to=iec-i --suffix=B --format='%.1f' "$total_saved_bytes")
echo "Total space saved: $total_saved_human ($total_saved_bytes)"
fi
exit $1
}
trap 'exit_script 130' SIGINT # Captures ctrl+c to allow for cleanup
find_zip_files() {
if [ -d "$1" ]; then
find "$1" -maxdepth 1 -name "*.zip"
elif [[ "$1" == *.zip ]]; then
echo "$1"
fi
}
process_arguments() {
declare -A seen_files # Create a dictionary
local zip_files=""
for arg in "$@"; do
local resolved_path=$(realpath "$arg")
# Check if path exists
if [ ! -e "$resolved_path" ]; then
# Write to stderr so it's separate from the file paths returned
echo "Directory or file not found: $resolved_path" >&2
continue
fi
local found_files=$(find_zip_files "$resolved_path") # Depending on the user args, may return duplicate files
if [ -z "$found_files" ]; then
echo "No ZIP archive(s) found: $resolved_path" >&2
continue
fi
# Record each ZIP filepath that has been seen
# If a filepath has already been added to zip_files, don't add it again
# This prevents recompressing the same file if a duplicate path is found
while IFS= read -r zip_file; do
if [ -z "${seen_files[$zip_file]}" ]; then
seen_files[$zip_file]=1 # Mark filepath as seen
zip_files+="$zip_file"$'\n' # Add unique filepath, appending with a literal new line character
fi
done <<< "$found_files"
done
echo "$zip_files" | sed -z "s/\n$//" | sort # Return the unique ZIP file paths, sorted, and final newline of the list removed
}
# Main logic starts here
total_saved_bytes=0
ZIP_FILES=$(process_arguments "$@")
if [ -z "$ZIP_FILES" ]; then # No ZIPs found
exit_script 1
fi
FILE_COUNT=$(echo "$ZIP_FILES" | wc -l)
FILE_COUNT_WIDTH=${#FILE_COUNT}
INDENT_WIDTH=$(( FILE_COUNT_WIDTH * 2 + 1 ))
echo "Optimizing $FILE_COUNT ZIP archives..."
TEMP_DIR=$(mktemp -d) # Create tmp folder after we know there are ZIP files to work with
count=1
while IFS= read -r zip_file; do
printf "%0${FILE_COUNT_WIDTH}d/${FILE_COUNT} Processing ${zip_file}\n" "$count"
old_size_bytes=$(stat -c %s "$zip_file")
7z x -bso0 "$zip_file" -y -o"$TEMP_DIR" # bso0 = Only show errors and progress
file_basename=$(basename "$zip_file")
new_zip_file="$TEMP_DIR/$file_basename"
7z a -bso0 -tzip -mm=Deflate64 -mx=9 -mmt=on "$new_zip_file" "$TEMP_DIR/*" # bso0 = Only show errors and progress
new_size_bytes=$(stat -c %s "$new_zip_file")
if [ "$new_size_bytes" -lt "$old_size_bytes" ]; then
bytes_saved=$(( old_size_bytes - new_size_bytes ))
human_saved=$(numfmt --to=iec-i --suffix=B --format='%.1f' $bytes_saved)
printf "%${INDENT_WIDTH}s Reduced by ${human_saved} (${bytes_saved})\n"
mv -f "$new_zip_file" "$zip_file"
total_saved_bytes=$(( total_saved_bytes + bytes_saved ))
fi
rm -rf "$TEMP_DIR"/*
count=$(( count + 1 ))
done <<< "$ZIP_FILES"
exit_script 0