2016-11-20 4 views
3

Es tut mir leid, wenn der Fragetitel nicht informativ genug ist. Bitte zögern Sie nicht, eine bessere Variante vorzuschlagen.finden, xargs: Befehlskette für jede Datei ausführen

Ich möchte die folgende Aufgabe ausführen: In einem Verzeichnis habe ich eine Reihe von Dateien, die Fotos im JPEG-Format sind. Ich möchte aus EXIF ​​die Daten extrahieren, wann diese Fotos aufgenommen wurden, ein neues Verzeichnis für jedes Datum erstellen und eine Datei in das entsprechende Verzeichnis verschieben.

(EXIF-Datum und die Uhrzeit haben das Format YYYY:MM:DD hh:mm:ss, und ich möchte, dass die Verzeichnisnamen als YYYY-MM-DD formatiert werden, das ist, warum ich sed verwenden)

Ich weiß, eine Art, wie jede dieser Aufgaben separat durchzuführen, aber versäumt, sie zusammen zu setzen. Ich verbrachte einige Zeit zu untersuchen, wie Befehle mit find mit -exec oder xargs ausgeführt werden, aber immer noch nicht verstehen, wie Sie alles richtig verketten.

Schließlich konnte ich meine Aufgabe mit zwei Befehle erfüllen:

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 {}" \; 

Aber ich weiß nicht wie die Vervielfältigung, und ich weiß nicht wie -exec sh -c. Gibt es den richtigen Weg, dies in einer Zeile und ohne -exec sh -c zu tun?

Antwort

5

Anstatt sich auf Einzeiler zu konzentrieren, wäre eine bessere Lösung, die Logik in ein Skript zu schreiben, das die Ausführung und den Test erleichtert. Setzen Sie diese in einer Datei movetodate.sh genannt:

#!/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 

Dann es aufzurufen:

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

Es ist leicht getan mit exiftool:

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

Zum Beispiel wandelt der Befehl ein Layout wie folgt:

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

dazu:

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

Wenn Sie noch identify verwenden möchten, können die Befehle wie folgt umformuliert werden:

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" {} \; 

Beachten Sie die Verwendung von $0 Variable im Skript. Wir übergeben den Platzhalter {} als erstes Argument an das Skript.

Das Skript kann einfach transformiert werden, um mehrere Argumente (Pfade) mit Hilfe einer for file in "[email protected]"-Schleife zu akzeptieren. In diesem Fall sollte das \; Zeichen durch + ersetzt werden. Wenn Sie jedoch eine große Anzahl von Dateien haben, die das Limit $(getconf ARG_MAX) überschreiten, benötigen Sie entweder xargs oder die Dateien einzeln verarbeiten, wie im obigen Skript gezeigt. Die gleichen Überlegungen gelten für den Befehl exiftool.

0

mit parallel du nicht das Skript benötigen, sondern stattdessen tun:

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 
Verwandte Themen