stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|echo mv "${|' |
SN=SHOOTNAME sh -s -- *
Der obige Befehl sollte das tun, was Sie brauchen. Es ist vielleicht allgemeiner als Sie es gewünscht haben - aber bitte sehen Sie am Ende dieser Antwort ein spezifischeres Beispiel.
Es funktioniert, indem *
alle Dateien im aktuellen Verzeichnis globalisiert und ihre Änderungszeiten ausgedruckt werden. Für nur Dateien im aktuellen Verzeichnis, die das Suffix enthalten .NEF
, möchten Sie den *
Globstar an den Enden der Zeilen 1 und 5 in ändern *.NEF
. Es werden einige Shell-Variablen und Anführungszeichen an das Ende angehängt - deren Namen nur am anderen Ende der Pipeline in der sh
Subshell vorhanden sind.
Da wir die Dateinamen nur anhand ihrer Glob-Reihenfolge oder der ${1}
Shell-Typparameter angeben, funktioniert dies auch mit jedem Dateinamen - unabhängig davon, welche seltsamen Zeichen darin enthalten sind.
Im echo
Moment enthält der Befehl ein - es ist nerfed. Laufen ist im Wesentlichen ein No-Op - es zeigt Ihnen nur, was es tun möchte. Hier ist die Ausgabe aus meinem Home-Verzeichnis, bevor es an Folgendes weitergeleitet wird sh
:
echo mv "${2}" "20140611_${SN}_${LINENO}"
echo mv "${4}" "20140614_${SN}_${LINENO}"
echo mv "${11}" "20140617_${SN}_${LINENO}"
echo mv "${7}" "20140622_${SN}_${LINENO}"
echo mv "${8}" "20140622_${SN}_${LINENO}"
echo mv "${1}" "20140624_${SN}_${LINENO}"
echo mv "${10}" "20140704_${SN}_${LINENO}"
echo mv "${5}" "20140704_${SN}_${LINENO}"
echo mv "${9}" "20140704_${SN}_${LINENO}"
echo mv "${12}" "20140705_${SN}_${LINENO}"
echo mv "${3}" "20140705_${SN}_${LINENO}"
echo mv "${13}" "20140706_${SN}_${LINENO}"
echo mv "${6}" "20140706_${SN}_${LINENO}"
Hier ist es nach sh
:
mv Desktop-1 20140611_SHOOTNAME_1
mv Library 20140614_SHOOTNAME_2
mv target.txt 20140617_SHOOTNAME_3
mv script.sh 20140622_SHOOTNAME_4
mv script.sh~ 20140622_SHOOTNAME_5
mv Desktop 20140624_SHOOTNAME_6
mv shot-2014-06-22_17-11-06.jpg 20140704_SHOOTNAME_7
mv Terminology.log 20140704_SHOOTNAME_8
mv shot-2014-06-22_17-10-16.jpg 20140704_SHOOTNAME_9
mv test 20140705_SHOOTNAME_10
mv Downloads 20140705_SHOOTNAME_11
mv test.tar 20140706_SHOOTNAME_12
mv new
file 20140706_SHOOTNAME_13
Möglicherweise stellen Sie fest, dass in meiner Ausgabe einige Bilddateien angezeigt werden, die bereits nach ihrer Erstellungszeit benannt wurden, deren neu zugewiesener Name jedoch nicht übereinstimmt. Dies ist keine Auswirkung von sort
- was wie vorgeschrieben funktioniert -, sondern dass diese Dateien zuletzt an diesem Datum eine Statusänderung hatten. Wie Sie in den Kommentaren zu dieser Frage angegeben haben, ctime
handelt es sich jedoch um die Eigenschaft, nach der Sie suchen. Dies ist die hier angebotene Eigenschaft für Sortierung und Name. Hier ist jedoch stat
die Ausgabe mit angehängten Dateinamen:
stat -c '%z %n' -- *
2014-06-24 16:50:09.110283839 -0700 Desktop
2014-06-11 23:34:02.981981145 -0700 Desktop-1
2014-07-05 01:00:43.213344635 -0700 Downloads
2014-06-14 10:32:13.537014418 -0700 Library
2014-07-04 23:02:25.079690701 -0700 Terminology.log
2014-07-06 11:24:05.398936386 -0700 new
file
2014-06-22 11:26:53.658004123 -0700 script.sh
2014-06-22 11:26:53.658004123 -0700 script.sh~
2014-07-04 13:34:00.063296353 -0700 shot-2014-06-22_17-10-16.jpg
2014-07-04 13:34:00.066629687 -0700 shot-2014-06-22_17-11-06.jpg
2014-06-17 19:59:38.475358571 -0700 target.txt
2014-07-05 23:53:39.097065292 -0700 test
2014-07-06 00:38:57.060521397 -0700 test.tar
Die obige Ausgabe soll mir auch helfen, zu demonstrieren, was die gesamte Pipeline tut.
So stat --printf='}" "%z_${SN}_${LINENO}"\n'
druckt Linien , die wie folgt aussehen:
}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... wo YMDHMS.NS -TZ
sind die verschiedenen strftime
Komponenten , die sie repräsentieren für ctime
. Das Ausgabeformat ist identisch für %w
- Zeitpunkt der Geburt der Datei - %x
- Zeitpunkt des letzten Zugriffs - oder %y
- Zeitpunkt der letzten Änderung. Wenn Sie also eines dieser Elemente %z
in der obigen Anweisung ersetzen, wird es stattdessen auf ihre Werte erweitert. Wie wir bereits in den Kommentaren besprochen haben, ist die %w
Geburtszeit der Datei kein verlässliches Attribut, und wenn sie nicht unterstützt wird, wird sie nur auf erweitert 0
.
Dies geschieht für jede Datei, in der sich die Shell-Globs befinden, *
oder für die von Ihnen bereitgestellten Shell-Globs - beispielsweise *.NEF
nur für Dateien im aktuellen Verzeichnis mit dem .NEF
Suffix.
Diese Liste wird übergeben, an nl
welche Zahlen jede Zeile um 1 erhöht wird. Ihre -n
Zeilennummern sind linksbündig ln
und nicht mit einer Mindest- -w
ID von 1 und nur einer ''
Nullzeichenfolge -s
aufgefüllt, um sie vom Inhalt der Zeile zu trennen. Es gibt aus:
I}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... wo I
ist die Nummer jeder Zeile.
sort
sortiert seine Eingabe vom -k2,3
zweiten Feld bis zum dritten - oder weiter YYYY-MM-DD HH:MM:SS.NS
. Da zu diesem Zeitpunkt die einzige eindeutige Qualität einer Zeile entweder I
das Datum oder das Datum ist und I
übersprungen wird, müssen keine genaueren Angaben gemacht werden. Dies betrifft auch den Kommentar, den Sie zu Dateien abgegeben haben, die nach Nummer und nicht nach Datum benannt sind. Ich sollte das getan haben , sort
bevor sed
in erster Linie , aber es fiel mir nicht auf Minuten zu sortieren und Sekunden und dergleichen.
Meine Testbasis dafür wurde wie folgt generiert:
for s in 9 8 7 6 5 4 3 2 1; do touch $s && sleep 1; done
Wenn ich sort
nach sed
- wie ich vor diesem bearbeiten tue - dann würde diese Dateien umbenennen , so dass 9
ich ${DATE}.SHOOTNAME.9
da jede Datei YMD
Felder sind identisch und sort
würden nicht ihre Linie Ordnung beeinflussen. Bei dieser Einstellung ist dieser Befehl jedoch sort
spezifisch für die Nanosekunde und wird daher 9
in ${DATE}.SHOOTNAME.1
und umgekehrt für umbenannt 1
. Danke, @Seul, dass du mich darauf aufmerksam gemacht hast.
sed
Entfernt dann die erste Zeichenfolge, die aussieht, <space><any char><any char>:<colon>
und alle nachfolgenden Zeichen, die ^
kein _
Unterstrich sind. An diesem Punkt sieht die Linie also so aus:
I}" "YYYY-MM-DD_${SN}_${LINENO}"
... als nächstes werden alle -
Striche entfernt. Und schließlich wird es echo mv "${
am ^
Kopf jeder Zeile eingefügt, also sieht es so aus:
echo mv "${I}" "YYYYMMDD_${SN}_${LINENO}"
- Zuletzt wird eine
sh
Ell mit der $SN
deklarierten Umgebungsvariablen aufgerufen - hier ist ihr Wert SHOOTNAME
. POSIX gibt an, dass die Shell die Variable $LINENO
für jede Zeile, die sie einliest, erhöht. Daher sollte der Wert im Dateinamen für jede Zeile, die wir eingeben, auf eins mehr als die letzte erweitert werden. Wenn dies - wie aus Ihren Kommentaren hervorgeht - aus irgendeinem Grund nicht auftritt, wird ein vollständig gültiger Ersatz $((i=i+1))
wie zuerst stat
in Zeile 1 der Pipeline gedruckt und wie ich in einem speziellen Beispiel unten angegeben habe.
Die Shell wird auch mit ihren Positionsparametern aufgerufen, die auf unseren Glob eingestellt sind - hier der *
Globstar für alle Dateien im aktuellen Verzeichnis. Wie bereits erwähnt, würde *.NEF
in dieser und in der ersten Zeile nur Dateien im aktuellen Verzeichnis mit dem Dateinamensuffix von bearbeitet .NEF
.
Solange der Globus mit dem in der ersten Zeile identisch ist, werden sie in der Reihenfolge sortiert, in der sie nl
nummeriert sind. Unabhängig davon, in welcher Zeile es auftritt, "${1}"
wird es auf denselben Dateinamen erweitert, den wir ihm entsprechend der nl
Ausgabe zugewiesen haben . Auf diese Weise können Sie die Dateien in Datumsreihenfolge schnell und sicher und in der richtigen Reihenfolge umbenennen.
- Wie auch schon erwähnt, habe ich den Befehl
echo
hier mit nerfed . Aber wenn Sie das ausführen echo
und feststellen, dass es zu Ihnen passt, müssen Sie das entfernen echo
.
So was:
stat --printf='}" "%z_${SN}_${LINENO}"\n' -- * |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
SN=SHOOTNAME sh -s -- *
Oder vielleicht:
export SN=SHOOTNAME SUFX=.NEF
stat --printf='}" "%z_${SN}_$((i=i+1))${SUFX}"\n' -- *$SUFX |
nl -nln -w1 -s '' |
sort -k2,3 |
sed 's| ..:[^_]*||;s|-||g;s|^|mv "${|' |
sh -s -- *$SUFX
Und hier wird es in eine Shell-Funktion eingearbeitet:
_batch_date_rename () ( # a big one
ERR= # for error reporting
export "DIR=$1" "SUFX=$2" \ # args 1,2 must be dirname and file suffix
"NAME=${3-${ERR:?no rename string specified}}" \ # need name string
"TIME=${4-%y}" INT=$((${INT:-25}*3)) ${NOCONFIRM+NOCONFIRM=}
#all above vars are exported to all points below
_path_chk () { #run once at start - fn quits if any below test fails
[ -d "$1" ] && [ -w "$1" ] && set -- "$1"/*"$2" && [ -e "$1" ]
} # chks for user writable dirname and resolvable $1/*$2 glob
_print_fmt () { #shell printf now not stat - last field zero padded
printf 'mv "${%d}" "${DIR}/%d_${NAME}_%04d${SUFX}"\n' "$@"
}
_print_mv () { #prints copy of mv action before attempting
echo '(set -x' #uses shells debug printer to show expanded vals
printf ': ${0+%s}\n' "$@" \
${NOCONFIRM-'Key "ENTER" to accept or "CTRL+C" to quit'}
echo \) #above can be disabled by declaring NOCONFIRM at invocation
} #by default fn batches 25 mvs at a time, displays them, and confirms
_read_loop () { #parses piped in with IFS, batches in INTerval of 25
argc=${1-$argc} ; ${1+shift} #total globbed files - quit point
while IFS=' -' read nl y m d na ; do #split on -
set -- "$@" "$nl" "$y$m$d" "$((i=i+1))" #build array until
[ "$#" -ge "$INT" ] && break #hit interval
done ; IFS='
'; set -- $(_print_fmt "$@") && unset IFS #finalize array in _print_fmt
_print_mv "$@" #do the debug out
${NOCONFIRM+:} read < /dev/tty #if $NOCONFIRM not set confirm
printf '%s\n' "$@" #now print the actual command
[ $((argc>i)) -eq 1 ] || echo 'exit 0' #check if quit point
_read_loop #if not quit repeat
}
_pipeline () { #this is mostly same - no sed though
stat -c "$TIME" -- "$@" | nl -nln -w1 -s ' ' | sort -k2,3 | {
_read_loop $# || echo 'exit 1' #read loop stands in for sed
} | sh -s -- "$@" #sh still evaluates on args
} #only two calls from main function below
_path_chk "$1" "$2" || ${ERR:?Invalid pathname parameters specified}
_pipeline "$DIR"/*"$SUFX" #if _path_chk do _pipeline
) #that's all folks
Das nutzt die Shell für einige Dinge, die ich mit anderen Dienstprogrammen gemacht habe. Das Konzept ist das gleiche - eine Dateiliste erstellen, auf verschiedene Arten sortieren und die Sortierreihenfolge speichern. Was an diesem Gedanken wirklich anders ist, ist, dass er Verschiebungsvorgänge stapelweise nach Intervallen stapelt, dem Benutzer anzeigt, was zu tun ist, und auf eine Eingabeaufforderung wartet, bevor er fortfährt. Ich habe mich hier damit aufgezeichnet, damit Sie eine Terminalsitzung sehen können, wie es funktioniert.