'Überschriebene' Dateien, Speicherplatz noch belegt, sind sie verloren?

11

So dumm, dass ich ungeduldig das folgende Skript auf meinem 19.04-Server verwendet habe, um eine Reihe von Videodateien in Ordner mit Präfixen zu verschieben:

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)
shopt -s nocasematch

for file in *
do
    for dir in "${dirs[@]}"
    do

     if [ -d "$file" ]; then
      echo 'this is a dir, skipping'
      break
     else
      if [[ $file =~ ^[$dir] ]]; then
       echo "----> $file moves into -> $dir <----"
       mv "$file" "$dir"
       break
      fi
     fi
  done
done

Keine Ahnung, wo es schief gelaufen ist, aber anstatt die Dateien in Ordner zu verschieben, ging es zu einer einzelnen Ausgabe. Also:

----> a1.ts moves into -> A <----
----> a2.ts moves into -> A <----
----> a3.ts moves into -> A <----
----> a4.ts moves into -> A <----
----> a5.ts moves into -> A <----
----> c1.ts moves into -> C <----
----> c2.ts moves into -> C <----
----> c3.ts moves into -> C <----
----> c4.ts moves into -> C <----
----> c5.ts moves into -> C <----

Zum Glück habe ich den Vorgang gestoppt (STRG + C), sobald ich bemerkte, dass er nicht wie beabsichtigt lief und nicht den gesamten Ordner durchlief.

Jetzt habe ich also diese Dateien Aund C, die weniger als ein GB groß sind, und wie es aussieht, handelt es sich um ein EINZIGES Video.

In der gesamten Festplattennutzung des Ordners selbst sind 50 GB nicht berücksichtigt, aber der gesamte Festplattenspeicher des Computers ist gleich geblieben. Lassen Sie mich denken, dass die Dateien nicht gelöscht werden?

Jede Hilfe geschätzt, danke :)

Bearbeiten: Die Dateien sind tatsächlich verschwunden, es bleibt nur die letzte zu schreibende Datei übrig. Es dauerte nur einige Zeit, bis die Informationen zur Datenträgerverwendung aktualisiert wurden. Moral der Geschichte, führen Sie Ihre Skripte zuvor auf Scheindateien aus!

Ich bin ein TI-Rechner
quelle
3
Und haben die Verzeichnisse genannt A, Bund so weiter existierte , bevor das Skript ausgeführt wird ? Wenn nicht, haben Sie die Dateien einfach umbenannt. Alle Dateien, mit denen Namen begonnen haben aoder Ain die umbenannt wurde A, sodass nur die zuletzt umbenannte Datei erhalten bleibt, die anderen werden überschrieben. Das Aufrufen einer Variablen direrstellt kein Verzeichnis!
Mook765
2
so hat es es auch interpretiert. "zuletzt umbenannte Datei überlebt" ha. Verzeichnisse gab es nicht, ich hätte für jedes vorher eine 'Berührung' hinzufügen sollen. Vielen Dank für die Klarstellung
Ich bin ein TI-Rechner
4
+1 für ".. Moral der Geschichte, führen Sie Ihre Skripte auf Scheindateien vor!"
Sudodus
4
Ein Tipp, um solche Probleme zu vermeiden: Verwenden Sie mv "$file" "$dir/"mit einem nachlaufenden /; Wenn $dirdies nicht der Fall ist, mvwird ein Fehler angezeigt $file, anstatt ihn in umzubenennen $dir. Berücksichtigen Sie auch mv -iund mv -n. Und machen Sie immer eine mkdir -pvor dem Umzug, für ein gutes Maß.
Marcelm
3
@sudodus Noch besser moralisch: "Sichern Sie immer Ihre Daten!".
Jon Bentley

Antworten:

15

Ich denke, das ist das Problem: Sie sollten Verzeichnisse A, B, C ... Z erstellt haben. Wenn Sie dies getan haben, sollte der mvBefehl die Dateien in diese Verzeichnisse verschoben haben.

Wenn nicht, verschiebt der mvBefehl die Dateien in Dateien mit den Namen A, B, C ... und ich denke, das haben Sie getan.

Um das Shellscript sicherer zu machen, sollten Sie die Verzeichnisse erstellen (falls diese noch nicht vorhanden sind), bevor Sie mit dem Verschieben beginnen.

dirs=(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z)

for dir in "${dirs[@]}"
do
 mkdir -p $dir
done

Wenn Sie möchten, dass die Dinge noch sicherer werden, können Sie sie auch mvmit dieser -iOption verwenden

   -i, --interactive
          prompt before overwrite
Sudodus
quelle
1
touchWäre das Hinzufügen ein guter Ersatz, mkdirum Konflikte zu vermeiden, wenn das Skript mehrmals ausgeführt wird?
Ich bin ein TI-Rechner
2
touchErstellt eine Datei, wenn der Name nicht vorhanden ist. In diesem Fall wird es also nicht das tun, was Sie wollen. mkdir -pkann mit dem Skript mehrmals umgehen.
Sudodus
6
Eine andere einfache Möglichkeit, die Sie mvsicherer machen können, besteht darin, sich daran zu gewöhnen, dem Zielnamen einen abschließenden Schrägstrich hinzuzufügen, wenn das Ziel ein Verzeichnis ist, d. H.mv "$file" "$dir/"
Zielnamen abschließenden
7

@Sudodus hat bereits erklärt, was schief gelaufen ist, aber hier ist eine einfachere Version Ihres Skripts für das nächste Mal:

for letter in {a..z}; do 
    dir=${letter^}
    mkdir -p -- "$dir" 
    mv -- "$letter"* "${letter^^}"* "$dir"/
done

Erläuterung

  • for letter in {a..z}; do: wird {a..z}auf alle Kleinbuchstaben zwischen aund erweitert z:

    $ echo {a..z}
    a b c d e f g h i j k l m n o p q r s t u v w x y z

    Dies wird also alle Kleinbuchstaben durchlaufen und jeweils als speichern $letter.

  • dir=${letter^}: Die Syntax ${var^^}gibt den Inhalt der Variablen $varmit dem ersten Zeichen in Großbuchstaben zurück (da dies nur ein Zeichen hat, ist das alles, was wir brauchen). Also, wenn $letterjaa , dann ${letter^^}ist Aund daher $dirdie Großbuchstabenversion des Stroms ist $letter.

  • mkdir -p -- "$dir": Erstellen Sie das Verzeichnis. Wenn es bereits existiert, tun Sie nichts (-p ). Das -- kennzeichnet das Ende der Optionen und ist nützlich, um sich vor Namen zu schützen, die mit beginnen -.
  • mv -- "$letter"* "${letter^}"* "$dir" : Verschieben Sie jede Datei (oder jedes Verzeichnis) zum entsprechenden Ziel.

Das Problem dabei ist, dass es auch alle Verzeichnisse verschiebt, die Sie möglicherweise haben. Die Zielverzeichnisse werden nicht verschoben, da sie entweder noch nicht vorhanden sind oder Sie versuchen, sie in sich selbst zu verschieben, aber alle vorhandenen Verzeichnisse, die nicht das Zielverzeichnis sind, werden verschoben.

Wenn das ein Problem ist, müssen Sie Folgendes tun:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done
Terdon
quelle
Was ist der Unterschied zwischen ${letter^}und ${letter^^}, und wenn sie identisch sind, warum ${letter^^}anstelle von verwenden $dir?
Fund Monica Klage
1
@NicHartley schreibt ${var^}nur den ersten Buchstaben ${var^^}groß , während alle Buchstaben groß geschrieben werden. Es macht hier keinen Unterschied, da es $letternur einen Buchstaben gibt.
Terdon
Dies ist eine perfekte Antwort, außer dass Sie möglicherweise eine zusätzliche Ebene der Vorsicht hinzufügen möchten, indem Sie $dirim mvBefehl einen Verzeichnis-Schrägstrich hinzufügen . (In seiner jetzigen Form wird es fehlschlagen, wenn eine Datei mit einem Großbuchstaben aus einem Buchstaben vorbesteht)
Stig Hemmer
@ StigHemmer whoops, ja in der Tat. Sehr guter Punkt, danke. Antwort bearbeitet.
Terdon
4

Anstatt jede Datei mit einem Wörterbucharray zu vergleichen, das eine Menge Iteration erzeugt, können Sie Dateien mit Mustern abgleichen.

Sehr einfache Sorte:

#!/bin/bash

videos=./videos
sorted=./sorted

# sort types link,move.
sort_type=link

find "$videos" -maxdepth 1 -type f \
   \( -name '*.avi' -o -name '*.mkv' -o -name '*.mp4' \) -print0 |

while IFS= read -r -d ''; do

    b=$(basename "$REPLY")
    c=${b::1}

    case $c in
        [a-zA-Z]) label=${c^} ;; [0-9]) label="0-9" ;; *) label="_" ;;
    esac

    [[ ! -d "$sorted/$label" ]] && mkdir -p "$sorted/$label"

    if [[ -L $sorted/$label/$b ]] || [[ -e $sorted/$label/$b ]]; then
        echo "File/link: '$b' exists, skipping."
        continue
    fi

    case $sort_type in
        link)
            ln -rfst "$sorted/$label" -- "$REPLY"
            ;;
        move)
               mv -t "$sorted/$label" -- "$REPLY"
            ;;
    esac
done
bac0n
quelle
Vielleicht habe ich es falsch gemacht, aber das hat definitiv nicht funktioniert, zehn Dateien verloren lol. Ich habe Probleme, den REPLY-Teil zu unterschätzen? Was ist die Absicht? Alles, was übrig bleibt (in den Ordnern ABCD ....), sind die Aliase-Dateien, auf die selbst verwiesen wird. Der Alias
Ich bin ein TI-Rechner.
REPLY wird auf die Eingabezeile gesetzt, die vom Befehl read builtin gelesen wird, wenn keine Argumente angegeben werden.
bac0n
ok und dann machst du am Ende ln -rfst "$ sortiert / $ label" - "$ ANTWORT" warum den Alias, wenn wir sie nur mit mv verschoben haben?
Ich bin ein TI-Rechner
Standardmäßig werden Links aus dem Verzeichnis "Videos" in Ihr sortiertes Verzeichnis erstellt. Wenn Sie sie mv möchten, müssen Sie "Videos verschieben"
auskommentieren
... nur nicht beide gleichzeitig.
Bac0n
2

Schutz in Ihrer .bashrc:

alias mv="mv -n --backup=numbered"

quelle
1
@ Zanna, Danke dafür! Zitate hinzugefügt.
Einige Fragen, nur um sicherzugehen (als Neuling), das Hinzufügen in die .zshrc- Datei ist auch gültig (wenn Sie ZSH verwenden)? in man mv heißt es -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)also ist es wichtig, dass das -n- tag vor den folgenden tags steht? Das --backup = nummeriert erzeugt ein Doppel von jedem Recht, ist das nicht ein bisschen übertrieben (und platzsparend / verbrauchend), wenn es um übergroße Videodateien (sprechende Terabyte) geht. Vielen Dank !
Ich bin ein TI-Rechner
2

Einige Möglichkeiten, um das mvÜberschreiben vorhandener Dateien zu verhindern:

  • Wenn Sie in ein Verzeichnis wechseln möchten, fügen Sie dem Ziel einen Schrägstrich hinzu, dh verwenden Sie mv "$file" "$dir"/anstelle von mv "$file" "$dir". Wenn $dires kein Verzeichnis gibt oder kein Verzeichnis ist, mvwird sich beschweren:

    $ touch a
    $ mv a z/
    mv: cannot move 'a' to 'z/': Not a directory
    $ touch z
    $ mv a z/
    mv: failed to access 'z/': Not a directory

    Dies scheint den Systemaufruf zu veranlassen rename("a", "z/"), daher sollte er vor Sicherheitslücken bei der Überprüfung bis zur Verwendung geschützt sein, falls jemand denselben Satz von Dateien gleichzeitig verarbeitet.

  • Alternativ verwenden mv -t "$dir" "$file". Wieder wird es sich beschweren, wenn $dires kein Verzeichnis ist.

  • Verwenden Sie die -nOption, um das Überschreiben vorhandener Dateien zu verhindern:

    -n, --no-clobber
        do not overwrite an existing file

    Es wird nicht daran gehindert, die erste Datei umzubenennen, aber es wird sie dann nicht mit den anderen in den Papierkorb werfen.

    Dies scheint eine Ebene zu sein rename(), daher ist es bei gleichzeitiger Handhabung möglicherweise nicht sicher. (Es gibt renameat2()eine Flagge, die ein Überschreiben verhindert.)

ilkkachu
quelle
1

Obwohl dies bei Ihnen anscheinend nicht der Fall ist, ist es möglich, dass Sie dies tun und die Dateien nicht verlieren. Dies würde eines von zwei Dingen erfordern, um wahr zu sein:

  • Eine oder mehrere "feste Links" zu denselben Dateien befinden sich an anderer Stelle im Dateisystem
  • Bei einem oder mehreren Prozessen ist eine Datei geöffnet

In Unix-Dateisystemen können mehrere Verzeichniseinträge auf genau denselben Dateiinhalt verweisen . Dies wird als " harte Verbindung " bezeichnet. Sie können mit dem lnBefehl feste Verknüpfungen erstellen , ohne die allgemeine -sOption (weich / symbolisch). Solange mindestens ein fester Link zum Dateiinhalt vorhanden ist, wird er vom Dateisystem nicht wiederverwendet.

(Randnotiz: Berechtigungen gelten normalerweise für den Dateiinhalt und nicht für den Verzeichniseintrag. Aus diesem Grund kann ein normaler Benutzer manchmal eine Datei löschen, deren Eigentümer er ist root, aber nicht darauf schreiben. Durch den Löschvorgang wird der Ordner und nicht die Datei selbst geändert. )

Das Dateisystem wird den Dateiinhalt auch nicht wiederverwenden, solange mindestens ein Prozess die Datei geöffnet hat. Selbst wenn kein Verzeichniseintrag vorhanden ist, betrachtet das Dateisystem den Speicherplatz erst dann als frei, wenn kein Prozess ihn geöffnet hat. Die Datei kann aus dem virtuellen Dateisystem wiederhergestellt/proc/<pid>/fd werden root, solange die Datei geöffnet bleibt. (Danke @fluffysheap.)

wberry
quelle
1
Wenn es einen Prozess mit geöffneter Datei gibt, können Sie ihn wiederherstellen, indem Sie ihn in / proc / <pid> / fd nachschlagen. Siehe superuser.com/questions/283102/...
fluffysheap