2016-11-20 9 views
3

Извините, если заголовок вопроса недостаточно информативен. Не стесняйтесь предлагать лучший вариант.find, xargs: выполнить цепочку команд для каждого файла

Я хочу выполнить следующую задачу: В каталоге у меня есть несколько файлов, которые являются фотографиями в формате JPEG. Я хочу извлечь из EXIF ​​даты, когда эти фотографии были сделаны, создать новый каталог для каждой даты и перенести файл в соответствующий каталог.

(дата и время EXIF ​​имеют формат YYYY:MM:DD hh:mm:ss, и я хочу, имена каталогов, которые будут отформатированы как YYYY-MM-DD, поэтому я использую СЭД)

Я вроде знаю, как выполнить каждую из этих задач по отдельности, но не смогли собрать их вместе. Я потратил некоторое время на изучение того, как выполнять команды с использованием find с -exec или xargs, но до сих пор не понял, как правильно цепочки.

Наконец-то я смог выполнить свою задачу с помощью двух команд:

find . -name '*.jpg' -exec sh -c "identify -format %[exif:DateTimeOriginal] {} 
    | sed 's/ [0-9:]*//; s/:/-/g' | xargs mkdir -p" \; 

find . -name '*.jpg' -exec sh -c "identify -format %[exif:DateTimeOriginal] {} 
    | sed 's/ [0-9:]*//; s/:/-/g; s/$/\//' | xargs mv {}" \; 

Но я не люблю дублирования, и я не люблю -exec sh -c. Есть ли правильный способ сделать это в одной строке и без использования -exec sh -c?

ответ

5

Вместо того, чтобы сосредоточиться на однострочном пространстве, лучшим решением было бы превратить логику в скрипт, который упростит выполнение и тестирование. Поместите это в файл с именем movetodate.sh:

#!/usr/bin/env bash 

# This script takes one or more image file paths 

set -e 
set -o pipefail 

for path in "[email protected]"; do 
    date=$(identify -format %[exif:DateTimeOriginal] | sed 's/ [0-9:]*//; s/:/-/g') 
    dest=$(dirname "$path")/$date 
    mkdir -p "$dest" 
    mv "$path" "$dest" 
done 

Затем, чтобы вызвать его:

find . -name '*.jpg' -exec ./movetodate.sh {} + 
2

Это легко сделать с exiftool:

exiftool "-Directory<DateTimeOriginal" -d %Y-%m-%d *.jpg 

Например, команда преобразует макет например:

. 
├── a.jpg (2013:10:17 10:01:00) 
└── b.jpg (2012:08:07 16:11:15) 

к этому:

. 
├── 2012-08-07 
│   └── b.jpg 
└── 2013-10-17 
    └── a.jpg 

Если вы все еще хотите использовать identify, команды можно переписать следующим образом:

script=$(cat <<'SCRIPT' 
d=$(
    d=$(identify -format "%[exif:DateTimeOriginal]" "$0" 2>/dev/null) || exit $? 
    d=${d:0:10} 
    printf '%s/%s' "$(dirname "$0")" "${d//:/-}" 
) || exit $? 
mkdir -p "$d" && mv -v "$0" "$d" 
SCRIPT 
) 

find "$dir" -name '*.jpg' -exec bash -c "$script" {} \; 

Обратите внимание на использование $0 переменной в скрипте. Мы передаем записям {} сценарию в качестве первого аргумента.

Сценарий можно легко преобразовать, чтобы принять несколько аргументов (путей) с помощью цикла for file in "[email protected]". В этом случае символ \; следует заменить на +. Однако, если у вас есть большое количество файлов, превышающих ограничение $(getconf ARG_MAX), вам потребуется либо xargs, либо обрабатывать файлы по одному, как показано в приведенном выше скрипте. Те же соображения применяются к команде exiftool.

0

С параллельно вам не нужен сценарий, но вместо этого сделать:

doit() { 
    path="$1" 
    date=$(identify -format %[exif:DateTimeOriginal] | sed 's/ [0-9:]*//; s/:/-/g') 
    dest=$(dirname "$path")/$date 
    mkdir -p "$dest" 
    mv "$path" "$dest" 
} 
export -f doit 
find . -name '*.jpg' | parallel doit