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 shSubshell 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 echoMoment 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, ctimehandelt es sich jedoch um die Eigenschaft, nach der Sie suchen. Dies ist die hier angebotene Eigenschaft für Sortierung und Name. Hier ist jedoch statdie 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 -TZsind die verschiedenen strftimeKomponenten , 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 %zin der obigen Anweisung ersetzen, wird es stattdessen auf ihre Werte erweitert. Wie wir bereits in den Kommentaren besprochen haben, ist die %wGeburtszeit 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 *.NEFnur für Dateien im aktuellen Verzeichnis mit dem .NEFSuffix.
Diese Liste wird übergeben, an nlwelche Zahlen jede Zeile um 1 erhöht wird. Ihre -nZeilennummern sind linksbündig lnund nicht mit einer Mindest- -wID von 1 und nur einer ''Nullzeichenfolge -saufgefüllt, um sie vom Inhalt der Zeile zu trennen. Es gibt aus:
I}" "YYYY-MM-DD HH:MM:SS.NS -TZ_${SN}_${LINENO}"
... wo Iist die Nummer jeder Zeile.
sortsortiert seine Eingabe vom -k2,3zweiten 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 Idas 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 , sortbevor sedin 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 9ich ${DATE}.SHOOTNAME.9da jede Datei YMDFelder sind identisch und sortwürden nicht ihre Linie Ordnung beeinflussen. Bei dieser Einstellung ist dieser Befehl jedoch sortspezifisch für die Nanosekunde und wird daher 9in ${DATE}.SHOOTNAME.1und umgekehrt für umbenannt 1. Danke, @Seul, dass du mich darauf aufmerksam gemacht hast.
sedEntfernt 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
shEll mit der $SNdeklarierten Umgebungsvariablen aufgerufen - hier ist ihr Wert SHOOTNAME. POSIX gibt an, dass die Shell die Variable $LINENOfü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 statin 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 *.NEFin 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 nlnummeriert sind. Unabhängig davon, in welcher Zeile es auftritt, "${1}"wird es auf denselben Dateinamen erweitert, den wir ihm entsprechend der nlAusgabe 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
echohier mit nerfed . Aber wenn Sie das ausführen echound 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.