optimize_images.sh 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. #!/bin/bash
  2. # Borrowed from https://gist.github.com/lgiraudel/6065155
  3. # Inplace mode added by Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
  4. PROGNAME=${0##*/}
  5. INPUT=''
  6. QUIET='0'
  7. NOSTATS='0'
  8. INPLACE='0' # (1=Images are replaced, 0=New images are stored into $OUTPUT)
  9. max_input_size=0
  10. max_output_size=0
  11. usage()
  12. {
  13. cat <<EO
  14. Usage: $PROGNAME (list|fix) [options]
  15. Example: optimize_images.sh (list|fix) -i dirtoscan
  16. Script to optimize JPG and PNG images in a directory.
  17. Options:
  18. EO
  19. cat <<EO | column -s\& -t
  20. -h, --help & shows this help
  21. -q, --quiet & disables output
  22. -i, --input [dir] & specify input directory (current directory by default)
  23. -o, --output [dir] & specify output directory ("output" by default)
  24. -ns, --no-stats & no stats at the end
  25. -p, --inplace & optimizes files inplace
  26. EO
  27. }
  28. # $1: input image
  29. # $2: output image
  30. optimize_image()
  31. {
  32. input_file_size=$(stat -c%s "$1")
  33. max_input_size=$(expr $max_input_size + $input_file_size)
  34. if [ "${1##*.}" = "png" ]; then
  35. #optipng -o1 -clobber -quiet "$1" -out "$2.firstpass"
  36. echo optipng -o1 -quiet "$1" -out "$2.firstpass"
  37. optipng -o1 -quiet "$1" -out "$2.firstpass"
  38. pngcrush -q -rem alla -reduce "$2.firstpass" "$2" >/dev/null
  39. rm -fr "$2.firstpass"
  40. fi
  41. if [ "${1##*.}" = "jpg" -o "${1##*.}" = "jpeg" ]; then
  42. jpegtran -copy none -progressive "$1" > $2
  43. fi
  44. output_file_size=$(stat -c%s "$2")
  45. max_output_size=$(expr $max_output_size + $output_file_size)
  46. }
  47. get_max_file_length()
  48. {
  49. local maxlength=0
  50. IMAGES=$(find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v $OUTPUT)
  51. for CURRENT_IMAGE in $IMAGES; do
  52. filename=$(basename "$CURRENT_IMAGE")
  53. if [[ ${#filename} -gt $maxlength ]]; then
  54. maxlength=${#filename}
  55. fi
  56. done
  57. echo "$maxlength"
  58. }
  59. main()
  60. {
  61. test=`type pngcrush >/dev/null 2>&1`
  62. result=$?
  63. if [ "x$result" == "x1" ]; then
  64. echo "Tool pngcrush not found" && exit
  65. fi
  66. test=`type optipng >/dev/null 2>&1`
  67. result=$?
  68. if [ "x$result" == "x1" ]; then
  69. echo "Tool optipng not found" && exit
  70. fi
  71. test=`type jpegtran >/dev/null 2>&1`
  72. result=$?
  73. if [ "x$result" == "x1" ]; then
  74. echo "Tool jpegtran not found" && exit
  75. fi
  76. # If $INPUT is empty, then we use current directory
  77. if [[ "$INPUT" == "" ]]; then
  78. INPUT=$(pwd)
  79. fi
  80. # If $OUTPUT is empty, then we use the directory "output" in the current directory
  81. if [[ "$OUTPUT" == "" ]]; then
  82. OUTPUT=$(pwd)/output
  83. fi
  84. # If inplace, we use /tmp for output
  85. if [[ "$INPLACE" == "1" ]]; then
  86. OUTPUT='/tmp/optimize'
  87. fi
  88. echo "Mode is $INPLACE (1=Images are replaced, 0=New images are stored into $OUTPUT)"
  89. # We create the output directory
  90. mkdir -p $OUTPUT
  91. # To avoid some troubles with filename with spaces, we store the current IFS (Internal File Separator)...
  92. SAVEIFS=$IFS
  93. # ...and we set a new one
  94. IFS=$(echo -en "\n\b")
  95. max_filelength=`get_max_file_length`
  96. pad=$(printf '%0.1s' "."{1..600})
  97. sDone=' [ DONE ]'
  98. linelength=$(expr $max_filelength + ${#sDone} + 5)
  99. # Search of all jpg/jpeg/png in $INPUT
  100. # We remove images from $OUTPUT if $OUTPUT is a subdirectory of $INPUT
  101. echo "Scan $INPUT to find images with find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v '/gource/' | grep -v '/includes/' | grep -v '/custom/' | grep -v $OUTPUT"
  102. IMAGES=$(find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v '/gource/' | grep -v '/includes/' | grep -v '/custom/' | grep -v '/documents/' | grep -v $OUTPUT)
  103. if [ "$QUIET" == "0" ]; then
  104. echo --- Optimizing $INPUT ---
  105. echo
  106. fi
  107. for CURRENT_IMAGE in $IMAGES; do
  108. echo "Process $CURRENT_IMAGE"
  109. filename=$(basename $CURRENT_IMAGE)
  110. if [ "$QUIET" == "0" ]; then
  111. printf '%s ' "$filename"
  112. printf '%*.*s' 0 $((linelength - ${#filename} - ${#sDone} )) "$pad"
  113. fi
  114. optimize_image "$CURRENT_IMAGE" "$OUTPUT/$filename"
  115. # Replace file
  116. if [[ "$INPLACE" == "1" ]]; then
  117. mv "$OUTPUT/$filename" "$CURRENT_IMAGE"
  118. fi
  119. if [ "$QUIET" == "0" ]; then
  120. printf '%s\n' "$sDone"
  121. fi
  122. done
  123. # Cleanup
  124. if [[ "$INPLACE" == "1" ]]; then
  125. rm -rf $OUTPUT
  126. fi
  127. # we restore the saved IFS
  128. IFS=$SAVEIFS
  129. if [ "$NOSTATS" == "0" -a "$QUIET" == "0" ]; then
  130. echo
  131. echo "Input: " $(human_readable_filesize $max_input_size)
  132. echo "Output: " $(human_readable_filesize $max_output_size)
  133. space_saved=$(expr $max_input_size - $max_output_size)
  134. echo "Space save: " $(human_readable_filesize $space_saved)
  135. fi
  136. }
  137. human_readable_filesize()
  138. {
  139. echo -n $1 | awk 'function human(x) {
  140. s=" b Kb Mb Gb Tb"
  141. while (x>=1024 && length(s)>1)
  142. {x/=1024; s=substr(s,4)}
  143. s=substr(s,1,4)
  144. xf=(s==" b ")?"%5d ":"%.2f"
  145. return sprintf( xf"%s", x, s)
  146. }
  147. {gsub(/^[0-9]+/, human($1)); print}'
  148. }
  149. SHORTOPTS="h,i:,o:,q,s,p"
  150. LONGOPTS="help,input:,output:,quiet,no-stats,inplace"
  151. ARGS=$(getopt -s bash --options $SHORTOPTS --longoptions $LONGOPTS --name $PROGNAME -- "$@")
  152. # Syntax
  153. if [ "x$1" != "xlist" -a "x$1" != "xfix" ]
  154. then
  155. usage
  156. exit 0
  157. fi
  158. eval set -- "$ARGS"
  159. while true; do
  160. case $1 in
  161. -h|--help)
  162. usage
  163. exit 0
  164. ;;
  165. -i|--input)
  166. shift
  167. INPUT=$1
  168. ;;
  169. -o|--output)
  170. shift
  171. OUTPUT=$1
  172. ;;
  173. -q|--quiet)
  174. QUIET='1'
  175. ;;
  176. -s|--no-stats)
  177. NOSTATS='1'
  178. ;;
  179. -p|--inplace)
  180. INPLACE='1'
  181. ;;
  182. --)
  183. shift
  184. break
  185. ;;
  186. *)
  187. shift
  188. break
  189. ;;
  190. esac
  191. shift
  192. done
  193. # To convert
  194. if [ "x$1" = "xlist" ]
  195. then
  196. INPLACE=0
  197. fi
  198. main