Dateiname automatisch erhöhen

7

Ich muss einige doppelte Dateien sammeln und möchte Namenskollisionen vermeiden. Das Problem ist, dass diese Dateisammlung möglicherweise durch eine andere Ausführung meines Skripts hinzugefügt wird, bevor die Dateien bereinigt werden und die Anzahl nur weiter erhöht werden soll.

Ich entschied mich für eine einfache till-Schleife, um die Zahl wie folgt zu erhöhen.

until [[ ! -f ${PWD}/DUPES/${num}-$1 ]]; do
num=$((num +1))
done
mv --no-clobber $1 ${PWD}/DUPES/${num}-$1

Ich kann mir nicht vorstellen, jemals 1k Dateien in einem extremen Beispiel zu überschreiten, also sind meine Fragen ...

Ist dies ein schrecklich ineffizienter Weg, um dies zu erreichen? Sollte ich die vorhandenen Dateien analysieren, um die höchste führende Zahl zu erhalten, die von dort aus inkrementiert werden kann, oder ist dies für das von mir vorgestellte extreme Beispiel in Ordnung? Oder gibt es insgesamt einen besseren Weg?

Akovia
quelle
Stellen Sie sich zwei Instanzen vor, die gleichzeitig ausgeführt werden und gleichzeitig gestartet wurden. Sie werden beide feststellen, dass (sagen wir) num = 4 verfügbar ist und beide versuchen, denselben Zielnamen zu verwenden.
Roaima
Wenn Sie keine fortlaufenden Nummern benötigen, sondern nur keine Kollision, können Sie diese möglicherweise verwenden mktemp, möglicherweise mit Ihrem Dateinamen als Basis. (Wir gehen dann davon aus, dass mktempdies seine Arbeit effizient erledigt.)
Ulrich Schwarz
Vielleicht möchten Sie ein Versionskontrollsystem wie git
Basile Starynkevitch

Antworten:

5

Sie könnten es so machen:

set -C; num=0                                  ### set -o noclobber; init $num
[ -e "$1" ] &&                                 ### this needs to be true
until 2>&3 >"./DUPES/$((num+=1))-$1" &&        ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do :; done 3>/dev/null                         ### do nothing

Sie würden sofort sicherstellen, dass mehrere Instanzen nicht denselben Namen für eine bestimmte Datei sichern können, und Ihre Variable erhöhen.

Der /dev/null<stderr löscht nur die Beschwerde der Shell über eine vorhandene Datei, wenn er versucht, die Ausgabe abzuschneiden / umzuleiten, und ein vorhandenes Ziel findet. Während noclobber aktiviert ist , wird es eine andere Datei nicht überschreiben - es wird nur open()ein neues Geschäft, wenn Sie verwenden >|. Sie brauchen sich also nicht zu beschweren, da es darum geht, vorhandene Dateien zu erhöhen, bis ein nicht vorhandener Name gefunden wird.

In Bezug auf den Leistungsaspekt wäre es besser, wenn Sie nicht bei Null anfangen würden. Oder wenn Sie versucht haben, den Unterschied auszugleichen. Ich denke, das Obige könnte etwas verbessert werden wie:

set -C; num=0                                  ### set -o noclobber; init $num
until 2>&3 >"./DUPES/$((num+=1))-$1"  &&       ### try to open() num+=1
      mv --  "$1"  "./DUPES/$num-$1"           ### if opened, mv over it
do    [ -e  "./DUPES/$((num*2))-$1" ] &&       ### halve fail distance
      num=$((num*2))                           ### up to a point of course
done  3>/dev/null                              ### done

... aber bis zu 1000 müssen Sie sich wahrscheinlich keine schrecklichen Sorgen machen. Ich habe in ein paar Sekunden 65.000 über zufällige Namen.

Übrigens - Sie könnten denken, Sie könnten einfach:

>"./DUPES/$((num+=1))-$1" mv -- "$1" "./DUPES/$num-$1"

... aber es funktioniert nicht in einer bashShell.

num=0; echo >"/tmp/$((num+=1))" >&2 "$num"; echo "$num" /tmp/[01]

0
1 /tmp/1

Aus irgendeinem Grund basherfolgt die Zuweisung in einem anderen Kontext für Weiterleitungen - und daher erfolgen die Erweiterungen in einer seltsamen Reihenfolge. Sie benötigen also einen separaten einfachen Befehl, um den korrekten $numWert zu erweitern, mit dem ich hier komme &&. Ansonsten aber:

num=0; echo "$((num+=1))" "$num"

1 1
mikeserv
quelle
Ich glaube nicht, dass ich mir über mehrere Instanzen Sorgen machen muss, aber ich habe auch nie daran gedacht, dies zu berücksichtigen. Als Anfänger brauchte ich ein paar, um das zu verstehen, und ich habe viel gelernt, nur um es herauszufinden. Da niemand Leistungsprobleme für das Inkrementieren erwähnt hat, bis ein Schreibvorgang ausgeführt werden kann, anstatt herauszufinden, wo mit dem Inkrementieren begonnen werden soll, gehe ich davon aus, dass die Frage ebenfalls beantwortet wird. Vielen Dank für die Bearbeitung mit einer klareren Erklärung.
Akovia
@akovia - danke für den Dank! und es tut mir leid, dass ich überhaupt nicht mit dem Performance-Bit gesprochen habe - es ist eine gute Frage.
Mikeserv
5

GNU mv hat eine --backupOption, die nützlich sein kann. Die folgende Interaktion zeigt, wie die Dateien umbenannt werden, wenn das Ziel vorhanden ist:

$ touch a b c o
$ mv --backup=numbered --verbose a o
`a' -> `o' (backup: `o.~1~')
$ mv --backup=numbered --verbose b o
`b' -> `o' (backup: `o.~2~')
$ mv --backup=numbered --verbose c o
`c' -> `o' (backup: `o.~3~')

In diesem Beispiel wurde die Originaldatei oin o.~1~, ato o.~2~, bto o.~3~und cto umbenannt o. Dies wird also nicht auf die gleiche Weise umbenannt wie der von Ihnen veröffentlichte Code, dies kann jedoch abhängig von Ihren genauen Anforderungen akzeptabel sein.

dhag
quelle
Weitaus eleganter als meine Lösung ( mvnatürlich unter der Annahme von GNU )
Roaima
1

Stellen Sie sich zwei Instanzen Ihres Vorschlags vor, die gleichzeitig ausgeführt werden und gleichzeitig gestartet wurden. Sie werden beide feststellen, dass (sagen wir) num = 4 verfügbar ist und beide versuchen, denselben Zielnamen zu verwenden.

Ich kann diese Alternative derzeit nicht testen, aber so etwas könnte ausreichen

num=1 attempt=10
while ! mv --no-clobber "$1" "${PWD}/DUPES/${num}-$1" && [[ 0 -lt $attempt ]]
do
    num=$((num+1))
    attempt=$((attempt-1))
done
[[ 0 -eq $attempt ]] && echo "ERROR: Too many attempts to handle $1" >&2
Roaima
quelle