LINUX.ORG.RU
ФорумAdmin

bash оптимизация изображений

 


2

1

Привет! Есть скриптик, если указать в INPUT директорию, к примеру, /usr/share/nginx - скрипт обработает все подкаталоги и оптимизирует все изображения, НО, он их помещает в папку output

Убирая соответствующую строчку (чтобы не записывало в output), ничего не обрабатывается

Просьба помочь с правкой скрипта

Цель: чтобы скрипт обрабатывал подкаталоги и заменял старые изображения новыми, оптимизированными, не перемещая их в отдельную папку output

#!/bin/bash

PROGNAME=${0##*/}
INPUT=''
QUIET='0'
NOSTATS='0'
max_input_size=0
max_output_size=0

usage()
{
  cat <<EO
Usage: $PROGNAME [options]
Script to optimize JPG and PNG images in a directory.
Options:
EO
cat <<EO | column -s\& -t
	-h, --help  	   & shows this help
	-q, --quiet 	   & disables output
	-i, --input [dir]  & specify input directory (current directory by default)
	-o, --output [dir] & specify output directory ("output" by default)
	-ns, --no-stats    & no stats at the end
EO
}

# $1: input image
# $2: output image 
optimize_image()
{
	input_file_size=$(stat -c%s "$1")
	max_input_size=$(expr $max_input_size + $input_file_size)

	if [ "${1##*.}" = "png" ]; then
		optipng -o1 -clobber -quiet $1 -out $2
		pngcrush -q -rem alla -reduce $1 $2 >/dev/null
	fi
	if [ "${1##*.}" = "jpg" -o "${1##*.}" = "jpeg" ]; then
		jpegtran -copy none -progressive $1 > $2
	fi

	output_file_size=$(stat -c%s "$2")
	max_output_size=$(expr $max_output_size + $output_file_size)
}

get_max_file_length()
{
	local maxlength=0

	IMAGES=$(find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v $OUTPUT)

	for CURRENT_IMAGE in $IMAGES; do
		filename=$(basename "$CURRENT_IMAGE")
		if [[ ${#filename} -gt $maxlength ]]; then
			maxlength=${#filename}
		fi
	done

	echo "$maxlength"	
}

main()
{
	# If $INPUT is empty, then we use current directory
	if [[ "$INPUT" == "" ]]; then
		INPUT=$(pwd)
	fi

	# If $OUTPUT is empty, then we use the directory "output" in the current directory
	if [[ "$OUTPUT" == "" ]]; then
		OUTPUT=$(pwd)/output
	fi

	# We create the output directory
	mkdir -p $OUTPUT

	# To avoid some troubles with filename with spaces, we store the current IFS (Internal File Separator)...
	SAVEIFS=$IFS
	# ...and we set a new one
	IFS=$(echo -en "\n\b")

	max_filelength=`get_max_file_length`
	pad=$(printf '%0.1s' "."{1..600})
	sDone=' [ DONE ]'
	linelength=$(expr $max_filelength + ${#sDone} + 5)

	# Search of all jpg/jpeg/png in $INPUT
	# We remove images from $OUTPUT if $OUTPUT is a subdirectory of $INPUT
	IMAGES=$(find $INPUT -regextype posix-extended -regex '.*\.(jpg|jpeg|png)' | grep -v $OUTPUT)

	if [ "$QUIET" == "0" ]; then
		echo --- Optimizing $INPUT ---
		echo
	fi
	for CURRENT_IMAGE in $IMAGES; do
		filename=$(basename $CURRENT_IMAGE)
		if [ "$QUIET" == "0" ]; then
		    printf '%s ' "$filename"
		    printf '%*.*s' 0 $((linelength - ${#filename} - ${#sDone} )) "$pad"
		fi

		optimize_image $CURRENT_IMAGE $OUTPUT/$filename

		if [ "$QUIET" == "0" ]; then
		    printf '%s\n' "$sDone"
		fi
	done

	# we restore the saved IFS
	IFS=$SAVEIFS

	if [ "$NOSTATS" == "0" -a "$QUIET" == "0" ]; then
		echo
		echo "Input: " $(human_readable_filesize $max_input_size)
		echo "Output: " $(human_readable_filesize $max_output_size)
		space_saved=$(expr $max_input_size - $max_output_size)
		echo "Space save: " $(human_readable_filesize $space_saved)
	fi
}

human_readable_filesize()
{
echo -n $1 | awk 'function human(x) {
     s=" b  Kb Mb Gb Tb"
     while (x>=1024 && length(s)>1) 
           {x/=1024; s=substr(s,4)}
     s=substr(s,1,4)
     xf=(s==" b ")?"%5d   ":"%.2f"
     return sprintf( xf"%s", x, s)
  }
  {gsub(/^[0-9]+/, human($1)); print}'
}

SHORTOPTS="h,i:,o:,q,s"
LONGOPTS="help,input:,output:,quiet,no-stats"
ARGS=$(getopt -s bash --options $SHORTOPTS --longoptions $LONGOPTS --name $PROGNAME -- "$@")

eval set -- "$ARGS"
while true; do
	case $1 in
		-h|--help)
			usage
			exit 0
			;;
		-i|--input)
			shift
			INPUT=$1
			;;
		-o|--output)
			shift
			OUTPUT=$1
			;;
		-q|--quiet)
			QUIET='1'
			;;
		-s|--no-stats)
			NOSTATS='1'
			;;
		--)
			shift
			break
			;;
		*)
			shift
			break
			;;
	esac
	shift
done

main


Последнее исправление: momi (всего исправлений: 2)

Ответ на: комментарий от Yorween

Исправил за 2-3 минуты до вашего сообщения, возможно вы не обновили страницу «Цель: чтобы скрипт обрабатывал подкаталоги и заменял старые изображения новыми, оптимизированными, не перемещая их в отдельную папку output»

momi
() автор топика
Ответ на: комментарий от Yorween

Переделать скрипт можно так: заместо создания директории output создавай диреткорию backup, затем в директорию backup копиру файл что будешь обрабатывать.

А потом делай обработку файла из диреткории backup с выводом изменений в исходный файл.

anonymous
()
Ответ на: комментарий от Deleted

Я кажется даже знаю как, к чему такие простыни вообще. Наверное писал человек не слишком высокой квалификации, им платят за объём.

anonymous
()

и заменял старые изображения новыми

В конце main:

        for CURRENT_IMAGE in $IMAGES; do
		filename=$(basename $CURRENT_IMAGE)
		if [ "$QUIET" == "0" ]; then
		    printf '%s ' "$filename"
		    printf '%*.*s' 0 $((linelength - ${#filename} - ${#sDone} )) "$pad"
		fi

		optimize_image $CURRENT_IMAGE $OUTPUT/$filename

		if [ "$QUIET" == "0" ]; then
		    printf '%s\n' "$sDone"
		fi
                mv -fv $OUTPUT/$filename $CURRENT_IMAGE
	done

Deleted
()
Последнее исправление: Deleted (всего исправлений: 2)

ну если грубый хак, то вот так можно:

optimize_image()
{
	input_file_size=$(stat -c%s "$1")
	max_input_size=$(expr $max_input_size + $input_file_size)

	if [ "${1##*.}" = "png" ]; then
		optipng -o1 -clobber -quiet $1 -out $2
		pngcrush -q -rem alla -reduce $1 $2 >/dev/null
	fi
	if [ "${1##*.}" = "jpg" -o "${1##*.}" = "jpeg" ]; then
		jpegtran -copy none -progressive $1 > $2
	fi

	output_file_size=$(stat -c%s "$2")
	max_output_size=$(expr $max_output_size + $output_file_size)
	mv -b $2 $1 # <-----тупо заменяем файл, оригинал сохраняется как "файл.jpg~"
}

В скрипте остаются атавизмы, мне их вычищать лень.

Алсо скрипт не поддерживает пробелы в именах файлов.

Алсо хз что будет, если при обработке произойдёт сбой.

legolegs ★★★★★
()
Ответ на: комментарий от Deleted

Простыню не читал, но это 146% можно сделать однострочником.

Разумеется, ещё и в несколько потоков. Особенно, если выкинуть сбор статистики.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

legolegs (20.01.2019 18:14:54)

		optipng -o1 -clobber -quiet $1 -out $2
		pngcrush -q -rem alla -reduce $1 $2 >/dev/null

Перебор. Плюс pngcrush иногда сбоит:

		optipng -o1 -clobber -quiet $1 -out $2
		pngcrush -q -rem alla -reduce $2 $2.png >/dev/null
                if [ ! -z $2.png ]
                then
                    mv -f $2.png $2
                else
                   rm -f $2.png
                fi

Как то так.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 2)
#!/bin/bash
#Функции обработки
opt_optipng ()
{
	input="$1"
	echo -e "\e[01;34mОбрабатывается\e[00m \e[01;31m(optipng):\e[00m \e[01;32m$input\e[00m"
	optipng0.7.4 -o3 "$input" >/dev/null 2>&1
}

opt_advpng ()
{
	input="$1"
	echo -e "\e[01;34mОбрабатывается\e[00m \e[01;31m(advpng):\e[00m \e[01;32m$input\e[00m"
	advpng -z -4 "$input">/dev/null 2>&1
}

opt_advdef ()
{
	input="$1"
	echo -e "\e[01;34mОбрабатывается\e[00m \e[01;31m(advdef):\e[00m \e[01;32m$input\e[00m"
	advdef -z -4 "$input">/dev/null 2>&1
}

opt_jpegtran ()
{
	input="$1"
	echo -e "\e[01;34mОбрабатывается\e[00m \e[01;31m(jpegtran):\e[00m \e[01;32m$input\e[00m"
	jpegtran -optimize -progressive -copy none -outfile "$input" "$input" 2>&1
	#jpegtran -arithmetic -copy none -outfile "$input" "$input" 2>&1
}

opt_gifsicle ()
{
	input="$1"
	echo -e "\e[01;34mОбрабатывается\e[00m \e[01;31m(gifsicle):\e[00m \e[01;32m$input\e[00m"
	gifsicle --batch --optimize=3 "$input" >/dev/null 2>&1
}
#Очистка экрана и поиск файлов для обработки
clear
SIZEPNG=$(find . -type f -iname '*.png' -printf '%s\n' | tee >(numpng=$(wc -l)) | awk '{SUM+=$1} END {print SUM}');
SIZEGIF=$(find . -type f -iname '*.gif' -printf '%s\n' | tee >(numgif=$(wc -l)) | awk '{SUM+=$1} END {print SUM}');
SIZEJPG=$(find . -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -printf '%s\n' | tee >(numjpg=$(wc -l)) | awk '{SUM+=$1} END {print SUM}')
SIZE=$[SIZEPNG+SIZEGIF+SIZEJPG]

#Рабочая часть скрипта
export -f opt_optipng
find -type f -iname '*.png' | sort | parallel --no-notice opt_optipng '{}'
export -f opt_advpng
find -type f -iname '*.png' | sort | parallel --no-notice opt_advpng '{}'
export -f opt_advdef
find -type f -iname '*.png' | sort | parallel --no-notice opt_advdef '{}'
export -f opt_jpegtran
find -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) | sort | parallel --no-notice opt_jpegtran '{}'
export -f opt_gifsicle
find -type f -iname '*.gif' | sort | parallel --no-notice opt_gifsicle '{}'

#Оформление информации для вывода
echo -e "\e[01;33mГотово!\e[00m"
SIZEPNG2=$(find . -type f -iname '*.png' -printf '%s\n' | awk '{SUM+=$1} END {print SUM}');
SIZEGIF2=$(find . -type f -iname '*.gif' -printf '%s\n' | awk '{SUM+=$1} END {print SUM}');
SIZEJPG2=$(find . -type f \( -iname '*.jpg' -o -iname '*.jpeg' \) -printf '%s\n' | awk '{SUM+=$1} END {print SUM}')
SIZE2=$[SIZEPNG2+SIZEGIF2+SIZEJPG2]
if [ $SIZEPNG ]; then
	PERCENTPNG=$[100-SIZEPNG2*100/SIZEPNG]
fi
if [ $SIZEGIF ]; then
	PERCENTGIF=$[100-SIZEGIF2*100/SIZEGIF]
fi
if [ $SIZEJPG ]; then
	PERCENTJPG=$[100-SIZEJPG2*100/SIZEJPG]
fi
if [ $SIZE ]; then
	PERCENT=$[100-SIZE2*100/SIZE]
	echo -e "Исходный размер всех обрабатываемых файлов был \e[01;31m$SIZE\e[00m байт а после обработки составил \e[01;31m$SIZE2\e[00m байт. Среднее сжатие на \e[01;31m$PERCENT%\e[00m
Из них:"
fi
if [ $SIZEPNG ]; then
	echo -e "PNG: было: \e[01;31m$SIZEPNG\e[00m байт, стало \e[01;31m$SIZEPNG2\e[00m байт, сжалось на \e[01;31m$PERCENTPNG%\e[00m"
fi
if [ $SIZEGIF ]; then
	echo -e "GIF: было: \e[01;31m$SIZEGIF\e[00m байт, стало \e[01;31m$SIZEGIF2\e[00m байт, сжалось на \e[01;31m$PERCENTGIF%\e[00m"
fi
if [ $SIZEJPG ]; then
	echo -e "JPG: было: \e[01;31m$SIZEJPG\e[00m байт, стало \e[01;31m$SIZEJPG2\e[00m байт, сжалось на \e[01;31m$PERCENTJPG%\e[00m"
fi

Всё уже написано, задолго до вас. Выложил свой велосипед. Не забудтье команды поменять на нужные, тут optipng отдельный используется. Ну и этот скрипт для оптимизации без потерь качества картинки. Можно с потерями более эффективно жать.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

С каких пор питон удобная пускалка для программ? Bash тут лучше. А сочинять алгоримы оптимизации на питоне — они будут очень медленными и очень некачественными, т.к. там много человеко-часов и специалистов надо, чтобы по уму всё сделать.

peregrine ★★★★★
()
Ответ на: комментарий от peregrine

С каких пор питон удобная пускалка для программ?

С тех пор, как там появились условия, циклы и типы, которых в баше нет.

они будут очень медленными

Никто и не предлагает писать их питоне. Я предлагаю использовать питон по его прямому назначению - скриптовый язык.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

С тех пор, как там появились условия, циклы и типы, которых в баше нет.

if fi (условия), for (циклы), типы только вредны в небольших скриптах. Если что я сейчас сижу и код на питоне пишу. Но не вижу его как адекватную замену bash-у.

peregrine ★★★★★
()
Последнее исправление: peregrine (всего исправлений: 1)
Ответ на: комментарий от peregrine

С каких пор питон удобная пускалка для программ?

С тех пор, как появился xonsh.

Control flow от питона, удобство порождения процессов от bash.

t184256 ★★★★★
()
Последнее исправление: t184256 (всего исправлений: 1)
Ответ на: комментарий от t184256

С тех пор, как появился xonsh.

Xonsh currently has the following external dependencies,

Run Time:

        Python v3.4+
        PLY (optional, included with xonsh)

То ещё чудо-юдо!

Deleted
()
Ответ на: комментарий от peregrine

if fi (условия), for (циклы), типы только вредны в небольших скриптах.

Это был сарказм.

RazrFalcon ★★★★★
()
Ответ на: комментарий от t184256

Какой ужас, superset питона требует питон!

Вы сами то не запутались в своем питоне, где ноги, а где голова? А точно, у него же ног нет!

Deleted
()
Последнее исправление: Deleted (всего исправлений: 1)
# $1: input image
# $2: output image 
optimize_image()
{
...
	if [ "${1##*.}" = "png" ]; then
		optipng -o1 -clobber -quiet $1 -out $2
		pngcrush -q -rem alla -reduce $1 $2 >/dev/null
	fi


Мне одному кажется что optipng здесь бесполезен от слова совсем?

Kroz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.