Для хранения базы своих фотографий раньше использовал пакетную обработку от XnView. Сегодня решил написать скрипт, который будет автоматически разбрасывать мои фотографии в определенную папку с разброской в подпапки по годам.

Имеем Ubuntu 16.04. Для работы скрипта группировки нам понадобится exiv2 и imagemagick. Для их установки выполните команду:

sudo apt-get install exiv2 imagemagick

Далее, приведу сам скрипт с комментариями по ходу

#!/bin/bash
# Скрипт раскладывает фотографии по каталогам вида гггг/мм, изменяет разрешение и качество в
# соответствии с заданными параметрами, а так же переименовывает изображения в формат
# гггг-мм-дд_чч-мм-сс.jpg
# На пример: /mnt/DCIM/IMG-20131014112354.jpg -> $HOME/Photo/2013/10/2013-10-14_11-23-54.jpg
#
# Данные о дате снимка ищутся в следующем порядке:
# 1) Exif данные (Exif.Photo.DateTimeOriginal и Exif.Image.DateTime)
# 2) В названии фотографии. Распознает различные варианты написания даты
# 3) Из даты создания файла
#
# При запуске без аргументов обрабатывает текущий каталог и все его подкаталоги. В качестве аргумента
# можно указать другой рабочий каталог-источник.
# НЕ удаляет оригиналы изображений!
# НЕ увеличивает разрешение
# Для работы необходим пакет exiv2 (sudo apt-get install exiv2)
##################################

src_dir=${1-`pwd`}	# По умолчанию используется текущий каталог
dst_dir=$HOME/PHOTO	# Каталог в который будут скопированны обработанные фотографии

quality=95  		# Качество JPG изображения
# resize=1600x900		# Размер после конвертации - раскомментируйте эту строку, и закомментируйте строку ниже, если хотите задать размеры изображения
resize=100% # оставляем 100%-ный размер

mkdir -p "$src_dir/../$(basename $src_dir)_done"  # создаем папку, рядом с обрабатываемой, в которую поместим резервную копию изображения

# Ищем JPG файлы в указанной папке и всех подпапках!
find "$src_dir" -iname "*.jpg" | sort | while IFS= read -r file ; do

  # Пробуем взять дату из Exif.Photo.DateTimeOriginal или Exif.Image.DateTime
  for photo_date in "Exif.Photo.DateTimeOriginal" "Exif.Image.DateTime" ; do
    photo_date=$(exiv2 -g "$photo_date" -Pv "$file")
    if [ -n "$photo_date" ] ; then  # Если дата найдена, прекращаем перебор
      break
    fi
  done

  if [ -z "$photo_date" ] ; then # Если в exif дата не найдена ищем в названии файла

    # Ищем дату в названии файла и приводим её к виду ггггммддччммсс
    photo_date=$(basename "$file" ".jpg" | egrep -o -m1 [0-9]\{4\}\([-_:\ ]?[0-9]\)\{10\})


      if [ -n "$photo_date" ] ; then

	# Приводим дату к виду гггг:мм:дд чч:мм:сс для корректного добавляения в exif
	photo_date=$(echo "$photo_date" | tr -d "\_\-\:\ " | sed 's/\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1:\2:\3 \4:\5:/')

	# Добавляем дату из названия файла в exif
	exiv2 -M"add Exif.Image.DateTime Ascii "$photo_date"" "$file"

      else # Если даты в названии не нашли, берем дату изменения (создания) файла
	photo_date=$(date +"%Y:%m:%d %T" -r "$file")
	#exiv2 -M"add Exif.Image.DateTime Ascii $photo_date" "$file"
      fi
  fi

  # Приводим различные вариации даты к единому виду гггг мм дд чч мм сс
  photo_date=$(echo "$photo_date" | egrep -o -m1 [0-9]\{4\}\([-_:\ ]?[0-9]\)\{10\} | tr -d "\_\-\:\ " | sed 's/\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1 \2 \3 \4 \5 /')

  # Разбиваем полученную дату (гггг мм дд чч мм сс) на фрагменты
  # $1 - Год, $2 - Месяц, $3 - День, $4 - Час, $5 - Минута, $6 - Секунда
  set -- $photo_date

  mkdir -p "$dst_dir/$1/$1-$2-$3" # Создаем структуру папок гггг/ггг-мм-дд

  # Копируем фотографию, только если исходный файл новее существующего или отсутствует. При копировании сохраняется дата создания фотографии
  #cp -uvp "$file" "$dst_dir/$1/$2/$1-$2-$3_$4-$5-$6.jpg"

  # Изменяем качество и размер фотографий, переименовываем и копируем в папку назначения
  # convert -quality "$quality" -resize "$resize"\> -verbose "$file" "$dst_dir/$1/$2/$1-$2-$3_$4-$5-$6.jpg"
  convert -quality "$quality" -resize "$resize"\> -verbose "$file" "$dst_dir/$1/$1-$2-$3/$(basename $file)"

  # Устанавливаем дату создания файла такую же как и дата в exif
  # touch -t "$1$2$3$4$5.$6" "$dst_dir/$1/$2/$1-$2-$3_$4-$5-$6.jpg"
  touch -t "$1$2$3$4$5.$6" "$dst_dir/$1/$1-$2-$3/$(basename $file)"


  mv $file $src_dir/../$(basename $src_dir)_done/$(basename $file)

done

exit 0

Самое главное, чтобы у вашего фото-устройства была корректно задана дата :)

Для запуска скрипта, переходим в папку, в которой лежат ваши фотографии и запускаем скрипт:

sh /path-to-script.sh

Для того чтобы посмотреть полный набор exif-данных, можно воспользоваться инструментом exiftool:

exiftool image-path

Для установки данного инструмента, необходимо выполнить команду:

sudo apt install libimage-exiftool-perl

Update 2025-02-16. Обновление и оптимизация скрипта

Прошло уже много лет, как я пользуюсь этим скриптом для разброса всех своих фотографий. Многое в срипте уже изменилось, долго пользовался 2-мя скриптами - отдельно для фото и отдельно для видео. Сегодня вот решил объединить их и переписать код в более читабельный формат:

#!/bin/bash

src_dir=${1-`pwd`} 
dst_dir="$HOME/PHOTO" 
backup_dir="${src_dir}/../$(basename "$src_dir")_done"  

mkdir -p "$backup_dir"

# Функция для получения даты из EXIF-метаданных
get_exif_date() {
    local file=$1
    for photo_date in "Exif.Photo.DateTimeOriginal" "Exif.Image.DateTime"; do
        photo_date=$(exiv2 -g "$photo_date" -Pv "$file" 2>/dev/null)
        if [ -n "$photo_date" ]; then
            echo "$photo_date" | sed 's/ /:/' # Преобразуем пробел в двоеточие для единого формата
            return
        fi
    done
    echo ""
}

# Функция для получения даты из имени файла
get_date_from_filename() {
    local file=$1
    local filename=$(basename "$file")
    echo "$filename" | grep -oE "[0-9]{4}([0-9]{2}){2}([0-9]{2}){3}" | sed 's/\([0-9]\{4\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)\([0-9]\{2\}\)/\1:\2:\3 \4:\5:/'
}

# Функция для получения даты создания файла
get_file_creation_date() {
    local file=$1
    if [[ "$OSTYPE" == "darwin"* ]]; then
        stat -f %SB -t "%Y:%m:%d %H:%M:%S" "$file"
    else
        stat -c %y "$file" | awk '{print $1, $2}' | sed 's/\.000000000//'
    fi
}

# Функция для получения даты из видеофайла
get_video_date() {
    local file=$1
    local shoot_date=$(ffprobe -v quiet -select_streams v:0 -show_entries stream_tags=creation_time -of default=noprint_wrappers=1:nokey=1 "$file" 2>/dev/null)
    if [ -n "$shoot_date" ]; then
        echo "$shoot_date" | sed 's/T/ /' | sed 's/\..*//' # Преобразуем формат ISO 8601 (2024-04-06T09:31:07.000000Z) в YYYY:MM:DD HH:MM:SS
    else
        local mastered_date=$(mediainfo --fullscan "$file" 2>/dev/null | grep "Mastered date" | awk -F ": " '{print $2}')
        if [ -n "$mastered_date" ]; then
            echo "$mastered_date"
        else
            echo ""
        fi
    fi
}

# Функция для получения даты файла
get_date() {
    local file=$1
    local type=$2

    if [ "$type" == "photo" ]; then
        local exif_date=$(get_exif_date "$file")
        if [ -n "$exif_date" ]; then
            echo "$exif_date"
            return
        fi
    fi

    local filename_date=$(get_date_from_filename "$file")
    if [ -n "$filename_date" ]; then
        echo "$filename_date"
        return
    fi

    if [ "$type" == "video" ]; then
        local video_date=$(get_video_date "$file")
        if [ -n "$video_date" ]; then
            echo "$video_date"
            return
        fi
    fi

    local file_date=$(get_file_creation_date "$file")
    if [ -n "$file_date" ]; then
        echo "$file_date"
        return
    fi

    date +"%Y:%m:%d %H:%M:%S"
}

# Функция для перемещения файла
move_file() {
    local file=$1
    local date=$2
    local type=$3

    # Преобразуем дату в формат YYYY:MM:DD
    local year=$(echo "$date" | awk -F "[: -]" '{print $1}') # Разделители: двоеточие, пробел, дефис
    local month=$(echo "$date" | awk -F "[: -]" '{print $2}')
    local day=$(echo "$date" | awk -F "[: -]" '{print $3}')

    # Создаем директорию назначения
    local dst_path="$dst_dir/$year/$year-$month-$day"
    mkdir -p "$dst_path"

    echo "Перемещение файла '$file' в '$dst_path/$(basename "$file")'..."
    
    # Перемещение файла с перезаписью
    mv -f "$file" "$dst_path/$(basename "$file")"
    
    # Создаем резервную копию
    cp -p "$dst_path/$(basename "$file")" "$backup_dir/"
    echo "Файл '$file' успешно перемещен в '$dst_path'."
}

# Обработка фотографий
find "$src_dir" -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" | sort | while IFS= read -r file; do
    photo_date=$(get_date "$file" "photo")
    move_file "$file" "$photo_date" "photo"
done

# Обработка видео
find "$src_dir" -type f \( -iname "*.mp4" -o -iname "*.avi" -o -iname "*.mkv" -o -iname "*.mov" -o -iname "*.wmv" -o -iname "*.3gp" -o -iname "*.flv" -o -iname "*.webm" -o -iname "*.mpeg" -o -iname "*.mpg" \) | sort | while IFS= read -r file; do
    video_date=$(get_date "$file" "video")
    move_file "$file" "$video_date" "video"
done

exit 0

Скрипт проверен на MacOs, на других linux-подобных системах может потребоваться доработка

Количество показов: 2651
02.06.2018


Реклама: ООО 'РЕГ.РУ Домены Хостинг'. ИНН 6312068502. ERID: CQH36pWzJqCRJ4UXaHSnYxUB4bq5fyuvNiq5y4geRNH7vF
Реклама: ООО «КЛАУДПЭЙМЕНТС». ИНН 7708806062. ERID: CQH36pWzJqCRJ4UXeNjXpDqc5rqRHP2xLyH2ojpuzzBdYG

Разработка сайта

Подайте заявку на разработку сайта на базе готового решения от компании 1С-Битрикс или одного из партнеров компании. Максимально подробно опишите, чему будет посвящен сайт, если это интернет-магазин - что он будет продавать, нужна ли мультиязычность, будут ли разные типы цен (розница, опт, крупный опт), будет ли интеграция с 1С, будет ли выгрузка товаров на различные торговые площадки...

Сопровождение сайта

Вы можете подать заявку на сопровождение вашего сайта на базе 1С-Битрикс. Сопровождение включает в себя: проверка актуальности обновлений сайта, проверка актуальности резервной копии, консультации по сайту. Опишите в заявке, какие еще объемы планируются на сопровождении и на какой срок вы планируете заключить договор на сопровождение - мы подберем подходящий вам бюджет на сопровождение

Работы по сайту

Вы можете подать заявку на выполнение определенного объема работ по сайту. Опишите в заявке объем работ. Это может быть разработка какого-то нового функционала, доработки по имеющемуся функционалу, доработки под требования сео-специалистов. На основании заявки вам будет сформирован бюджет работ, а также названы сроки на выполнение тех или иных работ.