Les fichiers «écrasés», l'espace encore occupé, sont-ils perdus?

11

Donc, stupide et impatient, j'ai utilisé le script suivant sur mon serveur 19.04 pour tenter de déplacer un tas de fichiers vidéo dans des dossiers avec des préfixes:

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

Aucune idée de l'endroit où cela s'est mal passé, mais au lieu de déplacer les fichiers vers des dossiers, il est allé vers une sortie singulière.

----> 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 <----

Heureusement, j'ai arrêté le processus (CTRL + C) dès que j'ai remarqué que ça ne se passait pas comme prévu et que je n'ai pas parcouru tout le dossier.

Alors maintenant, j'ai ces fichiers Aet C, qui sont moins d'un Go, et à première vue, c'est une seule vidéo.

Il y a 50 Go non comptabilisés dans l'utilisation totale du disque du dossier lui-même, mais l'espace disque global de l'ordinateur est resté le même. Me faisant penser que les fichiers ne sont pas supprimés?

Toute aide appréciée, merci :)

Edit: les fichiers sont réellement partis, il ne reste que le dernier fichier à écrire, il a juste fallu un certain temps pour que les informations d'utilisation du disque se mettent à jour .. moral de l'histoire, exécutez vos scripts sur des fichiers fictifs avant!

Je suis une calculatrice TI
la source
3
Et les répertoires nommés A, Betc., existaient-ils avant d'exécuter le script? Sinon, vous venez de renommer les fichiers. Tous les fichiers dont les noms ont commencé par aou Aont été renommés A, donc seul le dernier fichier renommé a survécu, les autres sont remplacés. Appeler une variable dirne crée pas de répertoire!
mook765
2
c'est ainsi qu'il l'a interprété. "le dernier fichier renommé a survécu" ha. les répertoires n'existaient pas, j'aurais dû ajouter une «touche» pour chacun avant la main. merci pour la clarification
je suis une calculatrice TI
4
+1 pour ".. morale de l'histoire, exécutez vos scripts sur des fichiers fictifs avant!"
sudodus
4
Un conseil pour éviter des problèmes comme celui-ci: Utilisez mv "$file" "$dir/", avec un suivi /; alors si $dirn'existe pas, mvsera erreur au lieu de renommage $fileà $dir. Considérez également mv -iet mv -n. Et faites toujours un mkdir -pavant de bouger, pour faire bonne mesure.
marcelm
3
@sudodus Encore mieux moral, "Sauvegardez toujours vos données!".
Jon Bentley

Réponses:

15

Je pense que c'est le problème: vous devriez avoir créé les répertoires A, B, C ... Z. Si vous l'avez fait, la mvcommande aurait dû déplacer les fichiers vers ces répertoires.

Sinon, la mvcommande déplace les fichiers vers des fichiers portant ces noms, A, B, C ... et je pense que c'est ce que vous avez fait.

Pour rendre le shellscript plus sûr, vous devez lui faire créer les répertoires (s'ils ne sont pas déjà là) avant de commencer le déplacement.

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

Si vous voulez que les choses deviennent encore plus sûres, vous pouvez également utiliser mvl' -ioption

   -i, --interactive
          prompt before overwrite
sudodus
la source
1
l'ajout touchserait-il un bon substitut pour mkdiréviter les conflits en cas d'exécution multiple du script?
Je suis une calculatrice TI le
2
touchcrée un fichier si le nom n'existe pas. Il ne fera donc pas ce que vous voulez dans ce cas. mkdir -ppeut gérer plusieurs fois l'utilisation du script.
sudodus
6
Une autre façon simple de vous rendre mvplus sûr est de prendre l'habitude d'ajouter une barre oblique de fin au nom de la cible lorsque la cible est un répertoire, c'estmv "$file" "$dir/"
steeldriver
7

@Sudodus a déjà expliqué ce qui n'a pas fonctionné, mais voici une version plus simple de votre script pour la prochaine fois:

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

Explication

  • for letter in {a..z}; do: {a..z}s'étend à toutes les lettres minuscules entre aet 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

    Donc, cela itérera sur toutes les lettres minuscules, en enregistrant chacune sous $letter.

  • dir=${letter^}: la syntaxe ${var^^}renvoie le contenu de la variable $varavec le premier caractère en majuscule (puisque cela n'a qu'un seul caractère, c'est tout ce dont nous avons besoin). Donc, si $letterest a, alors ${letter^^}est A, et $dirsera donc la version majuscule du courant $letter.

  • mkdir -p -- "$dir": créez le répertoire. S'il existe déjà, ne faites rien ( -p). Le -- signifie la fin des options et est utile pour se protéger contre les noms commençant par -.
  • mv -- "$letter"* "${letter^}"* "$dir" : déplacer chaque fichier (ou répertoire) vers la cible appropriée.

Le problème avec cela, c'est qu'il déplacera également tous les répertoires que vous pourriez avoir. Il ne déplacera pas les répertoires cibles, car soit ils n'existent pas encore, soit vous essayez de les déplacer vers eux-mêmes, mais tous les répertoires existants qui ne sont pas le répertoire cible seront déplacés.

Si c'est un problème, vous devrez faire quelque chose comme ceci:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done
terdon
la source
Quelle est la différence entre ${letter^}et ${letter^^}, et s'ils sont identiques, pourquoi les ${letter^^}remplacer $dir?
Fund Monica's Lawsuit
1
@NicHartley met en ${var^}majuscule uniquement la première lettre, tandis que ${var^^}toutes les lettres sont mises en majuscule . Cela ne fait aucune différence ici car $letteril n'a qu'une seule lettre.
terdon
C'est une réponse parfaite, sauf que vous voudrez peut-être ajouter une couche supplémentaire de prudence en ajoutant une barre oblique de répertoire $dirdans la mvcommande. (Dans sa forme actuelle, il échouera si un fichier préexiste avec un nom en majuscule d'une seule lettre)
Stig Hemmer
@StigHemmer whoops, oui en effet. Très bon point, merci. Réponse modifiée.
terdon
4

Au lieu de vérifier chaque fichier par rapport à un tableau de dictionnaire qui crée beaucoup d'itérations, vous pouvez comparer les fichiers aux modèles.

Tri très basique:

#!/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
la source
Peut-être que je l'ai mal fait mais cela n'a définitivement pas fonctionné, j'ai perdu dix fichiers lol. J'ai du mal à comprendre la partie REPLY? quelle est l'intention Tout ce qui reste (à l'intérieur des dossiers ABCD ....) sont les fichiers d'alias eux-mêmes pointant vers .. tha alias
Je suis une calculatrice TI
REPLY défini sur la ligne d'entrée lue par la commande read readin quand aucun argument n'est fourni.
bac0n
ok et puis à la fin vous faites ln -rfst "$ sorted / $ label" - "$ REPLY" pourquoi faire l'alias si on vient de les déplacer avec mv?
Je suis une calculatrice TI du
Par défaut, il crée des liens du répertoire "vidéos" dans votre répertoire trié, si vous voulez les mv, vous devez décommenter "Déplacer les vidéos" et commenter "Lier les vidéos" (je pense que les liens symboliques sont moins effrayants)
bac0n
... mais pas les deux en même temps.
bac0n
2

Sauvegardez dans votre .bashrc:

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

la source
1
@Zanna, merci pour ça! Ajout de citations.
Quelques questions juste pour être sûr (étant un débutant), l'ajout dans le fichier .zshrc est également valide (si vous utilisez ZSH)? dans man mv, il est dit -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)que la balise -n sera avant de suivre les balises. Le --backup = numéroté créera un double de chaque droit, n'est-ce pas un peu exagéré (et énergie / consommation d'espace) lors du traitement de fichiers vidéo très volumineux (téraoctets parlants). Merci !
Je suis une calculatrice TI le
2

Pour mémoire, quelques façons de ne pas mvécraser les fichiers existants:

  • Si vous souhaitez vous déplacer dans un répertoire, ajoutez une barre oblique à la cible, c'est-à-dire utilisez à la mv "$file" "$dir"/place de mv "$file" "$dir". S'il $dirn'existe pas ou n'est pas un répertoire, mvse plaindra:

    $ 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

    Cela semble faire l'appel système rename("a", "z/"), donc il devrait être à l'abri des vulnérabilités de temps de vérification à temps d'utilisation, au cas où quelqu'un manipulerait le même ensemble de fichiers en même temps.

  • Vous pouvez également utiliser mv -t "$dir" "$file". Encore une fois, il se plaindra s'il $dirne s'agit pas d'un répertoire.

  • Utilisez l' -noption pour éviter d'écraser les fichiers existants:

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

    Cela ne l'empêchera pas de renommer le premier fichier, mais il ne le supprimera pas avec les autres.

    Cela semble appeler un simple rename(), donc il pourrait ne pas être sûr avec une manipulation simultanée. (Il y renameat2()aurait un drapeau pour empêcher l'écrasement.)

ilkkachu
la source
1

Bien que ce ne soit apparemment pas le cas pour vous, il est possible que vous puissiez le faire sans perdre les fichiers. Cela nécessiterait l'une des deux choses suivantes:

  • Un ou plusieurs «liens durs» vers les mêmes fichiers existent ailleurs sur le système de fichiers
  • Un ou plusieurs processus ont un fichier ouvert

Les systèmes de fichiers Unix permettent à plusieurs entrées de répertoire de se référer exactement au même contenu de fichier . C'est ce qu'on appelle un « lien dur ». Vous pouvez créer des liens durs avec la lncommande, sans l' -soption commune (douce / symbolique). Tant qu'il existe au moins un lien dur vers le contenu du fichier, il ne sera pas réutilisé par le système de fichiers.

(Remarque: les autorisations s'appliquent généralement au contenu du fichier, pas à l'entrée de répertoire. C'est pourquoi un utilisateur ordinaire peut parfois supprimer un fichier appartenant à root, mais ne pas y écrire. L'opération de suppression modifie le dossier, pas le fichier lui-même. )

Le système de fichiers ne réutilisera pas non plus le contenu du fichier tant qu’au moins un processus a ouvert le fichier. Même si aucune entrée de répertoire n'existe, le système de fichiers ne considérera pas l'espace comme libre jusqu'à ce qu'aucun processus ne l'ouvre. Le fichier peut être récupéré à partir du système /proc/<pid>/fdde fichiers virtuel roottant que le fichier reste ouvert. (Merci @fluffysheap.)

wberry
la source
1
S'il se trouve qu'il y a un processus avec le fichier ouvert, vous pouvez le récupérer en le recherchant dans / proc / <pid> / fd. Voir superuser.com/questions/283102/…
fluffysheap