I file "sovrascritti", lo spazio ancora occupato, sono persi?


11

Così stupido impaziente, ho usato il seguente script sul mio server 19.04 nel tentativo di spostare un gruppo di file video in cartelle con prefissi:

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

Non ho idea di dove sia andato storto, ma invece di spostare i file in cartelle è andato su un output singolare .. quindi:

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

Per fortuna ho interrotto il processo (CTRL + C) non appena ho notato che non stava andando come previsto e non ho attraversato l'intera cartella.

Quindi ora ho quei file Ae C, che sono meno di un GB, e a quanto pare sono un SINGOLO video.

Ci sono 50 GB non contabilizzati nell'uso totale del disco della cartella stessa, ma lo spazio su disco complessivo del computer è rimasto lo stesso. Mi fai pensare che i file non vengano cancellati?

Qualsiasi aiuto apprezzato, grazie :)

Modifica: i file in realtà sono spariti, rimane solo l'ultimo file da scrivere, è bastato un po 'di tempo per l'aggiornamento delle informazioni sull'uso del disco .. morale della storia, esegui i tuoi script su file finti prima!


3
E le directory hanno chiamato A, Be così via prima dell'esecuzione dello script? Altrimenti hai appena rinominato i file. Tutti i file i cui nomi sono iniziati ao Asono stati rinominati A, quindi è sopravvissuto solo l'ultimo file rinominato, gli altri vengono sovrascritti. Chiamare una variabile dirnon crea una directory!
mook765,

2
questo è anche il modo in cui l'ha interpretato. "ultimo file rinominato sopravvissuto" ah. le directory non esistevano, avrei dovuto aggiungere un "tocco" per ciascuno prima. grazie per il chiarimento
Sono un calcolatore TI

4
+1 per ".. morale della storia, esegui i tuoi script su file finti prima!"
sudodus,

4
Un consiglio per evitare problemi come questo: Usa mv "$file" "$dir/", con un finale /; quindi se $dirnon esiste, mvverrà visualizzato l'errore invece di rinominarlo $filein $dir. Considera anche mv -ie mv -n. E fai sempre qualcosa mkdir -pprima di muoverti, per buona misura.
marcelm,

3
@sudodus Anche meglio morale, "Esegui sempre il backup dei tuoi dati!".
Jon Bentley,

Risposte:


15

Penso che questo sia il problema: avresti dovuto creare le directory A, B, C ... Z. Se così fosse, il mvcomando avrebbe dovuto spostare i file in quelle directory.

In caso contrario, il mvcomando sposta i file in file con quei nomi, A, B, C ... e penso che sia quello che hai fatto.

Per rendere più sicuro lo shellscript, dovresti farlo creare le directory (se non sono già presenti) prima di iniziare lo spostamento.

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

Se vuoi che le cose diventino ancora più sicure, puoi anche usare mvcon l' -iopzione

   -i, --interactive
          prompt before overwrite

1
aggiungere touchsarebbe un buon sostituto per mkdirevitare conflitti in caso di esecuzione dello script più volte?
Sono un calcolatore TI del

2
touchcrea un file se il nome non esiste. Quindi non farà ciò che vuoi in questo caso. mkdir -ppuò gestire più volte lo script.
sudodus,

6
Un altro modo semplice per rendere mvpiù sicuro è prendere l'abitudine di aggiungere una barra finale al nome del target quando il target è una directory, ad esempiomv "$file" "$dir/"
steeldriver

7

@Sudodus ha già spiegato cosa è andato storto, ma ecco una versione più semplice del tuo script per la prossima volta:

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

Spiegazione

  • for letter in {a..z}; do: si {a..z}espande in tutte le lettere minuscole tra ae 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

    Quindi questo ripeterà tutte le lettere minuscole, salvandole ognuna come $letter.

  • dir=${letter^}: la sintassi ${var^^}restituisce il contenuto della variabile $varcon il primo carattere in maiuscolo (poiché questo ha solo un carattere, questo è tutto ciò di cui abbiamo bisogno). Quindi, se $letterè a, allora ${letter^^}è Ae quindi $dirsarà la versione maiuscola dell'attuale $letter.

  • mkdir -p -- "$dir": crea la directory. Se esiste già, non fare nulla ( -p). La -- significa la fine delle opzioni , ed è utile per la protezione contro i nomi che iniziano con -.
  • mv -- "$letter"* "${letter^}"* "$dir" : sposta ogni file (o directory) sulla destinazione pertinente.

Il problema è che sposta anche le directory che potresti avere. Non sposterà le directory di destinazione, perché o non esistono ancora, o proverai a spostarle in se stesse, ma verranno spostate tutte le directory esistenti che non sono la directory di destinazione.

Se questo è un problema, dovrai fare qualcosa del genere:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done

Qual è la differenza tra ${letter^}e ${letter^^}, e se sono identici, perché usare ${letter^^}al posto di $dir?
Finanzia la causa di Monica il

1
@NicHartley mette in ${var^}maiuscolo solo la prima lettera, mentre in ${var^^}maiuscolo tutte le lettere. Non fa alcuna differenza qui poiché $letterha solo una lettera.
terdon,

Questa è una risposta perfetta, tranne per il fatto che potresti voler aggiungere un ulteriore livello di attenzione aggiungendo una barra $dirnel mvcomando. (Nella sua forma attuale fallirà se un file preesiste con un nome maiuscolo di una lettera)
Stig Hemmer

@StigHemmer whoops, sì davvero. Ottimo punto, grazie. Risposta modificata.
terdon,

4

Invece di controllare ogni file con un array di dizionari che crea molta iterazione, puoi confrontare i file con i pattern.

Ordinamento molto semplice:

#!/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

Forse ho sbagliato, ma sicuramente non ha funzionato, ho perso dieci file lol. Ho problemi a capire la parte REPLY? qual è l'intento Tutto ciò che rimane (all'interno delle cartelle ABCD ....) sono i file degli alias stessi che puntano a .. tha alias
Sono un calcolatore TI

REPLY impostato sulla riga di input letta dal comando read readin quando non vengono forniti argomenti.
bac0n

ok e poi alla fine fai ln -rfst "$ ordinato / $ etichetta" - "$ RISPONDI" perché alias se li abbiamo spostati con mv?
Sono un calcolatore TI del

Come impostazione predefinita, crea collegamenti dalla directory "video" alla directory ordinata, se vuoi convertirli devi decommentare "Sposta video" e commentare "Collega video" (penso che i collegamenti simbolici siano meno spaventosi)
bac0n

... non entrambi allo stesso tempo.
bac0n

2

Salvaguarda nel tuo .bashrc:

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

1
@Zanna, grazie per quello! Citazioni aggiunte.

Poche domande solo per essere sicuri (essendo un principiante), anche l'aggiunta nel file .zshrc è valida (se si utilizza ZSH)? in man mv dice -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)così importa che il tag -n sarà prima di seguire i tag? I = --backup numerate creerà un doppio di ciascun diritto, non è che un (terabyte parlanti) po 'eccessivo (e lo spazio di energia / consumo) quando si tratta di file video di grandi dimensioni uber-è. Grazie !
Sono un calcolatore TI il

2

Per la cronaca, alcuni modi per impedire la mvsovrascrittura di file esistenti:

  • Se vuoi spostarti in una directory, aggiungi una barra alla destinazione, ovvero usa mv "$file" "$dir"/invece di mv "$file" "$dir". Se $dirnon esiste o non è una directory, mvsi lamenterà:

    $ 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

    Questo sembra far chiamare il sistema rename("a", "z/"), quindi dovrebbe essere al sicuro dalle vulnerabilità al momento del check-to-time-of-use, nel caso in cui qualcuno stia gestendo lo stesso set di file contemporaneamente.

  • In alternativa, utilizzare mv -t "$dir" "$file". Ancora una volta, si lamenterà se $dirnon è una directory.

  • Utilizzare l' -nopzione per impedire la sovrascrittura di file esistenti:

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

    Non gli impedirà di rinominare il primo file, ma non lo eliminerà con gli altri.

    Questo sembra chiamare un piano rename(), quindi potrebbe non essere sicuro con la gestione simultanea. (C'è renameat2()che supporterebbe una bandiera per impedire la sovrascrittura.)


1

Anche se apparentemente non è il caso per te, è possibile che tu possa farlo e non perdere i file. Ciò richiederebbe che una delle due cose sia vera:

  • Uno o più "collegamenti fisici" agli stessi file esistono altrove nel filesystem
  • Uno o più processi hanno un file aperto

I filesystem Unix consentono a più di una voce della directory di fare riferimento allo stesso identico contenuto del file . Questo si chiama " collegamento reale ". È possibile creare collegamenti reali con il lncomando, senza l' -sopzione comune (soft / simbolica). Finché esiste almeno un collegamento reale al contenuto del file, non verrà riutilizzato dal filesystem.

(Nota a margine, le autorizzazioni si applicano in genere al contenuto del file, non alla voce della directory. Ecco perché un utente normale a volte può eliminare un file di proprietà root, ma non scriverlo. L'operazione di cancellazione modifica la cartella, non il file stesso. )

Inoltre, il file system non riutilizzerà il contenuto del file, purché almeno un processo abbia il file aperto. Anche se non esiste alcuna voce nella directory, il filesystem non considererà lo spazio libero fino a quando nessun processo lo avrà aperto. Il file può essere recuperato dal file system virtuale /proc/<pid>/fdda rootfinché il file rimane aperto. (Grazie @fluffysheap.)


1
Se si verifica un processo con il file aperto, è possibile recuperarlo cercando in / proc / <pid> / fd. Vedi superuser.com/questions/283102/…
fluffysheap il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.