Neuen Namen für verschobene Datei generieren, um ein Überschreiben zu verhindern?

8

Wie kann ich einen neuen Namen für eine Datei generieren, wenn eine Datei mit demselben Namen vorhanden ist? In einer Desktop-Umgebung wird ein neuer Name generiert, indem eine Nummer an das Ende des Dateinamens angehängt wird. Wie kann dies jedoch über die Befehlszeile erreicht werden?

Ich verwende das Android-Betriebssystem mit Busybox.

kyle k
quelle
1
Können Sie Ihr Q erweitern, um anzugeben, in welcher Kapazität Sie diese Dateien in Bezug auf Android erstellen möchten? Java? Welches Werkzeug hast du, Busybox? Oder nur Vanille Android?
slm
@user slm Ich werde es zum Sichern der im Download-Ordner auf meinem Tablet enthaltenen Dateien verwenden. Das Programm sortiert die Dateien basierend auf der Erweiterung in die entsprechenden Ordner. Ich habe ein Python-Skript geschrieben, das dies tut, aber das Programm ist langsam und überschreibt auch eine Datei, wenn sie denselben Namen hat. Ich bin gerade dabei, das Programm in Bash umzuschreiben. Das Generieren eines neuen Namens ist der Teil, mit dem ich zu kämpfen hatte.
Kyle K
Vielen Dank für Ihre Antwort! Haben Sie dann Zugriff auf eine Version von mktemp?
slm
@user slm Ich habe Busybox installiert und es ist enthalten, aber wenn ich einen Systemaufruf von einem Programm mache, mktempwird nicht funktionieren.
Kyle K
Funktioniert nicht mit einem Fehler? Auch von wo aus tätigen Sie diesen Systemaufruf? Python?
slm

Antworten:

5

Angenommen, Sie haben eine POSIX-Shell, können Sie dies tun:

mv() {
        eval "DEST=\${$#}" #The destination is the last positional parameter
        if [ -e "$DEST" ] && ! [ -d "$DEST" ];then
                PREFIX=${DEST%.*}
                COUNT=1
                EXT=${DEST##*.}
                args= i=1
                while [ $i -lt $# ]; do args="$args \"\${$i}\"" i=$((i+1)); done
                DEST="$NAME-"$(printf "%03d" $COUNT)".$EXT"
                while [ -e "$DEST" ];do
                    COUNT=$((COUNT+1))
                    DEST="$NAME-"$(printf "%03d" $COUNT)".$EXT"
                done
                eval "command mv $args \"\$DEST\""
        else
                command mv "$@"
        fi
}

Wie man das benutzt

Dies ist eine Funktion, also speichern Sie sie in Ihrer ~/.bashrcund rufen Sie sie wie gewohnt auf mv.

Was das macht

  • Speichert den Pfad zur ursprünglichen mvausführbaren Datei in der MVVariablen
  • Ruft das letzte Argument, mit dem es aufgerufen wurde, in die Variable ab DEST
  • Wenn DESTvorhanden und kein Verzeichnis ist, geht diese Funktion davon aus, dass Ihr Umbenennen versucht, eine Datei zu blockieren
  • Anschließend wird das Präfix des endgültigen Namens (alles vor dem Finale ., das die Erweiterung kennzeichnet), die Erweiterung (alles nach dem Finale .) und die Anzahl (falls vorhanden; alles im Präfix nach dem Finale -) extrahiert .
  • Die extrahierte Anzahl wird auf Null gesetzt, wenn ansonsten keine Anzahl gefunden wurde. Sie wird auf die Anzahl gesetzt, die im vorherigen Schritt gefunden wurde.
  • Die aktuelle Anzahl wird erhöht
  • Die Funktion ruft sich dann mit allen ursprünglichen Argumenten (Schalter + Dateinamen) abzüglich des letzten auf und fügt den neuen Dateinamen anstelle des letzten Arguments im ursprünglichen Aufruf hinzu. Der neue Name ist der alte Name, jedoch wird vor der Erweiterung ein dreistelliger Zähler (mit Nullfüllung) hinzugefügt.
  • Die Funktion ist rekursiv, denn wenn Sie sagen, dass mv a.txt b.txtsie zuerst versucht wird mv a.txt b-001.txt. Dieser nächste mvAufruf muss auch die Funktion selbst sein, da b-001.txtwir, falls vorhanden, den Zähler weiter erhöhen möchten, bis wir einen neuen Dateinamen finden, der nicht vorhanden ist.
  • Wenn das letzte Argument nicht vorhanden ist oder ein Verzeichnis ist, wird die ursprüngliche mvausführbare Datei mit Ihren ursprünglichen Argumenten aufgerufen.

Vorsichtsmaßnahmen

  • Wie oft Sie wiederholt versuchen können, eine vorhandene Datei zu überladen, hängt von der Länge des Zählers ab (in diesem Fall 999 Mal). Sie können eine Anzahl von Ziffern auswählen, die das Inode-Limit Ihres Dateisystems umfassen, um sicherzustellen, dass dies so lange funktioniert, wie Sie Dateien erstellen können.
  • Wenn Sie versuchen, eine Datei zu überfallen, deren Name ähnlich ist, foo-001.txtwird sie in verschoben foo-001-001.txt.

Anmerkungen

  • Um das Namensmuster zu ändern, ändern Sie das 3in der printfAnweisung nach Belieben.
  • Dieser Code wurde getestet
  • Dies ist sehr simpel und ich bin sicher, dass es Randfälle geben wird, in denen es kläglich fehlschlägt. Gerne versuche ich, sie für Sie zu reparieren, wenn Sie welche finden. Versuchen Sie dies in der Zwischenzeit nicht auf einer Produktionsmaschine.
Joseph R.
quelle
gleiche Meinung hier: P
Rahul Patil
@ RahulPatil Außer ich versuche, das GUI-Verhalten zu duplizieren.
Joseph R.
Das ist großartig, ich habe die GUI noch nicht verwendet.
Rahul Patil
Es überschreibt die Zieldatei,
Rahul Patil
@RahulPatil Ich bin mir nicht sicher, was Sie sagen: In Ihrem Beispiel wurde festgestellt, dass /tmpes sich um ein vorhandenes Verzeichnis handelt, und /bin/mv file1 /tmpich habe nicht gesehen, wo das Problem liegt.
Joseph R.
3

Im Allgemeinen verwende ich das Tool mktemp, um zuverlässige temporäre Dateien zu erstellen. Standardmäßig werden Dateien erstellt, es können jedoch auch Verzeichnisse über den -dSwitch erstellt werden.

Beispiel

So können Sie einige temporäre Namen für Dateien in Ihrem aktuellen Verzeichnis erstellen.

$ mktemp somefile.XXXXX
somefile.kiDad

$ mktemp somefile.XXXX
somefile.MrSW

$ mktemp someotherfile.XXXXXXXXXXX
someotherfile.Um4aXKrt3lv

Dadurch werden die Dateien für Sie erstellt.

Verweise

slm
quelle
2
Nein mktempauf Android, aber trotzdem +1 von mir, da dies eine gute Antwort auf eine gute Frage ist, U & L weise.
Goldlöckchen
@goldilocks - danke für deine freundlichen Worte. Es ist immer schön!
slm
@goldilocks - wissen Sie sofort, ob Busybox enthalten ist? Es gibt eine Implementierung von mktemp darin, ich weiß nicht so viel über Android. code.google.com/p/abb/source/browse/mktemp.c?name=upstream/…
slm
1
Es sieht so aus, als ob eine Busybox auf Android erfordert, dass das Gerät gerootet ist (Jailbreak). Die native Shell ist /system/bin/shund scheint shkompatibel zu sein - Konstrukte wie if [ -w $file ]Arbeit mvund renamesind verfügbar -, daher sollte dort ein Skript funktionieren, das dies nur mit integrierten Funktionen ausführt.
Goldlöckchen
@ Goldlöckchen - danke für die Überprüfung. Was suchen Sie, um dies herauszufinden? Ich möchte meine Füße mit Android nass machen, möchte aber kein Telefon / Gerät haben.
slm
2

Hier ist eine Alternative zu Joseph Rs Drehbuch, das keine der Einschränkungen enthält! Es wird ein numerisches Suffix an einen Pfadnamen angehängt (der Pfad kann ein Verzeichnis oder eine Datei sein), wobei der Suffixwert erhöht wird, bis einer gefunden wird, der noch nicht vorhanden ist. Andere Dienstprogramme verwenden beispielsweise logrotateein ähnliches Muster, drehen jedoch alle vorhandenen Kopien so, dass die neue immer '0' für ein Suffix hat. Da dies in diesem Sinne keine Rotation ist, werde ich es nennen dotmv. Denken Sie daran, dass file.0dies die älteste Kopie sein wird.

Zum Beispiel:

dotmv somefile.txt

Benennt um somefile.txt somefile.txt.0, sofern letzteres nicht vorhanden ist. In diesem Fall ist dies der Fall somefile.txt.1und so weiter. Sie können mehr als eine Datei ( dotmv this that "the other thing"usw.) auflisten, alle werden punktverschoben.

Ich glaube, dies ist POSIX-konform - es läuft mit set -o posixon bash (aber das ist ein zweifelhafter Test). Ich habe auch mit der Android (Jelly Bean 4.2.1) Shell getestet und es funktioniert dort. Unter Android müssen Sie jedoch den Shebang wie angegeben ändern oder ausführen sh dotmv- was Sie auch tun werden, es sei denn, Sie haben ein gerootetes Gerät, da es sonst keine Möglichkeit gibt, ein Skript ausführbar zu machen. Wenn Sie den Shebang ändern, können Sie ihn verwenden exec dotmv.

#!/bin/sh
# On android change that to /system/bin/sh.

# Validate arguments
if [ $# -lt 1 ]; then
    echo "A list of one or more paths is required."
    exit 1
fi

# Checks if a path exists and can be moved.
checkPath () {
    if [ ! -e "$1" ]; then
        echo "'$1' does not exist."
        return 1;
    fi
    if [ ! -w "$1" ]; then
        echo "Cannot move '$1', permission denied."
        return 1;
    fi
    return 0;
}

# Finds a new path with numerical suffix.
getName () {
    suf=0;
    while [ -e "$1.$suf" ]
        do let suf+=1
    done
    Dest=$1.$suf
}

# Loop through arguments -- use quotes to allow spaces in paths.
while (($#)); do
    Src=$1
    Dest=$1
    shift
    checkPath "$Src"
    if [ $? -eq 0 ]; then
        getName "$Src"
        mv "$Src" "$Dest"
    fi
done

Hoffentlich ist die Logik hier sehr einfach. Dies kann in Python, C oder einer anderen vollständigen prozeduralen Sprache mit Datei-E / A implementiert werden.

Goldlöckchen
quelle