Der beste Weg, um Dateinamen auszutauschen

8

Ich muss die Dateinamen von zwei Dateien ( fileund file_1) austauschen . Ich benutze den folgenden Code dafür.

mv file .phfile
mv file_1 file
mv .phfile file

Dies funktioniert, ist aber sehr fehlerhaft. Manchmal führt es sogar zu Datenverlust. Gibt es einen besseren Weg, dies zu tun?

Binoy Babu
quelle
5
Etwas, das Sie beachten und befürchten sollten - jedes Programm, das ein offenes Dateihandle für eine dieser Dateien hat, behält das offene Dateihandle nach dem Umbenennen bei, da es auf denselben Inode verweist, den es beim ursprünglichen Öffnen der Datei hatte. Bis alle Programme, in denen die Dateien geöffnet sind, diese schließen, ist dies der Fall und führt zu einer Beschädigung der Daten, wenn Sie erwarten, dass durch das Umbenennen vorhandene Schreiberhandles beschädigt werden.
Synthesizerpatel

Antworten:

4

Der renameat2Syscall auf Linux-Systemen mit dem RENAME_EXCHANGEFlag sollte genau das tun. Dies ist ein CLI-Tool, das behauptet, es zu verwenden.

Nick Russler
quelle
Dass dies nicht die richtige Antwort ist, ist kriminell.
Xaekai
Hier ist die Manpage: man7.org/linux/man-pages/man2/rename.2.html
dreua
@ Xaekai Übertreibung beiseite, nein. Diese Funktion gab es nicht einmal, als die Frage gestellt wurde, und bis heute funktioniert sie nur unter Linux, nicht unter anderen Betriebssystemen, und es ist ein Dienstprogramm erforderlich, das Sie wahrscheinlich nicht installiert haben.
Gilles 'SO - hör auf böse zu sein'
@ Gilles'SO-stopbeingevil 'Mit freundlicher Genehmigung des UNIX-Erbes kann ich dieses Wesentliche erfassen, kompilieren, in ein Verzeichnis in meinem PATH, chmod + x, verschieben und in weniger als 40 Sekunden aufrufen. Töte deinen Standpunkt mit Vorurteilen. Auch wir sind jetzt 8 Jahre später , und es scheint dieser syscall 7 Jahre alt ist 🤷 ich die überwiegende Mehrheit erwarte hier auf Linux - Kernel zu kommen, da OSX legal UNIX sein kann , aber sicher nicht geistig und BSDs Marktanteil von lived- in Userspaces ist fast nichts mehr.
Xaekai
3

In herkömmlichen Unix-Systemen gibt es keine einfache Möglichkeit, Dateien auszutauschen. Daher müssen Sie einen temporären Zwischennamen verwenden. Stellen Sie aus Gründen der Robustheit sicher, dass der temporäre Name von keinem anderen Programm verwendet wird (verwenden Sie ihn also mktemp) und dass er sich im selben Dateisystem wie eine der Dateien befindet (andernfalls würden die Dateien unnötig kopiert, anstatt nur umbenannt zu werden).

swap_files () {
  tmp_name=$(TMPDIR=$(dirname -- "$1") mktemp) &&
  mv -f -- "$1" "$tmp_name" &&
  mv -f -- "$2" "$1" &&
  mv -f -- "$tmp_name" "$1"
}
swap_files file file_1

Beachten Sie, dass im Falle eines Fehlers die erste Datei möglicherweise noch unter ihrem temporären Namen steht und die zweite Datei möglicherweise noch nicht verschoben wurde. Wenn Sie bei Unterbrechungen und Abstürzen Robustheit benötigen, ist es möglicherweise einfacher, eine Variante mit zwei temporären Namen wiederherzustellen.

swap_files2 () {
  tmp_dir1=$(TMPDIR=$(dirname -- "$1") mktemp -d .swap_files.XXXXXXXXXXXX) &&
  tmp_dir2=$(TMPDIR=$(dirname -- "$2") mktemp -d .swap_files.XXXXXXXXXXXX) &&
  mv -f -- "$1" "$tmp_dir1/" &&
  mv -f -- "$2" "$tmp_dir2/" &&
  mv -f -- "$tmp_dir1/"* "$1" &&
  mv -f -- "$tmp_dir2/"* "$2" &&
  rmdir -- "$tmp_dir1" "$tmp_dir2"
}

Wenn die temporären Verzeichnisse .swap_files.????????????bei einem Neustart vorhanden sind, bedeutet dies, dass ein Dateiaustausch durch einen Stromausfall unterbrochen wurde. Beachten Sie, dass möglicherweise eine der Dateien bereits verschoben wurde und die andere nicht, sodass der Code hier nicht alle Fälle berücksichtigt. Dies hängt davon ab, welche Art von Wiederherstellung Sie wünschen.

Moderne Linux-Kernel (seit 3.15, veröffentlicht im Juni 2014) haben einen Systemaufruf zum Austauschen von Dateien : renameat2(…, RENAME_EXCHANGE). Es scheint jedoch kein allgemein verfügbares Befehlszeilenprogramm dafür zu geben. Sogar Glibc-Unterstützung wurde erst kürzlich hinzugefügt ( 2.28 , veröffentlicht im August 2018).

Gilles 'SO - hör auf böse zu sein'
quelle
einfacher Weg in der Tat: D
Kiwy
"Es gibt keine einfache Möglichkeit, Dateien auszutauschen
dreua
2

Folgendes habe ich letztendlich verwendet:

file1=1stfile
file2=2ndfile
temp="$(mktemp -dp /mnt/sdcard)"
mv "$file1" $temp
mv "$file2" "$file1"
mv $temp/"$file1" "$file2"
Binoy Babu
quelle
Ich würde einige Überprüfungen hinzufügen, ob die mv-Anweisungen erfolgreich waren, wie die anderen vorgeschlagenen Antworten.
Walter A
@WalterA Das Projekt ist jetzt EOL. Ich habe gerade die Antwort zur Vervollständigung hinzugefügt. Fühlen Sie sich frei zu bearbeiten.
Binoy Babu
1

Das ist robuster:

TMPFILE=tmp.$$
mv -- "$file1" $TMPFILE && mv -- "$file2" "$file1" && mv -- $TMPFILE "$file2"

Das Zitieren dient dazu, Probleme mit Leerzeichen in Dateinamen zu vermeiden. Es verwendet eine tmp-Datei und && führt den folgenden Befehl nur aus, wenn der vorhergehende erfolgreich beendet wurde.

Guido
quelle
Wie ist das? file1=1stfile && file2=2ndfile && temp="$(mktemp -dp /mnt/sdcard)" && mv "$file1" $temp && mv "$file2" "$file1" && mv $temp/"$file1" "$file2". Es klappt. Danke für deinen Beitrag.
Binoy Babu
siehe diese stackoverflow.com/q/9475497/1068546
Binoy Babu
mktemp zu benutzen ist gut; schöner Fund
Guido
Das Ausführen eines Befehls mit && in Java mit runtime.exec () funktioniert jedoch nicht. :(
Binoy Babu
Sie müssen Ihre Variable yourShellInput in Anführungszeichen setzen: String yourShellInput = "\" echo hi && echo ho \ ""; oder wenn Sie doppelte Anführungszeichen in Ihrem Befehl haben String yourShellInput = "'echo hi && echo ho'";
Guido