Archivos 'sobrescritos', espacio todavía ocupado, ¿están perdidos?

11

Así que, impaciente, utilicé el siguiente script en mi servidor 19.04 en un intento de mover un montón de archivos de video a carpetas con prefijos:

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

No tengo idea de dónde salió mal, pero en lugar de mover los archivos a las carpetas fue a una salida singular ... entonces:

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

Afortunadamente detuve el proceso (CTRL + C) tan pronto como noté que no iba como estaba previsto y no revisé toda la carpeta.

Así que ahora tengo esos archivos Ay C, que son menos de un GB, y por lo que parece son un solo video.

Hay 50 Gb no contabilizados en el uso total del disco de la carpeta en sí, pero el espacio total en disco de la computadora se ha mantenido igual. ¿Me haces pensar que los archivos no se eliminan?

Cualquier ayuda apreciada, gracias :)

Editar: los archivos se han ido, solo queda el último archivo que se escribió, todo lo que tomó fue un tiempo para que el disco usara la información para actualizar ... la moraleja de la historia, ¡ejecute sus scripts en archivos simulados antes!

Soy una calculadora TI
fuente
3
¿Y existían los directorios nombrados A, Betc., antes de ejecutar el script? Si no, simplemente cambió el nombre de los archivos. Todos los archivos cuyos nombres comenzaron con ao Ahan sido renombrados A, por lo que solo sobrevivió el último archivo renombrado, los demás se sobrescriben. ¡Llamar a una variable dirno crea un directorio!
mook765
2
así lo interpretó también. "último archivo renombrado sobrevivió" ha. los directorios no existían, debería haber agregado un 'toque' para cada uno de antemano. gracias por aclarar
soy una calculadora TI el
44
+1 para "... moraleja de la historia, ejecuta tus scripts en archivos simulados antes".
sudodus
44
Un consejo para evitar problemas como este: Uso mv "$file" "$dir/", con un final /; entonces, si $dirno existe, se mvproducirá un error en lugar de cambiar el nombre $filea $dir. También considere mv -iy mv -n. Y siempre haz una mkdir -pantes de mudarte, por si acaso.
marcelm
3
@sudodus Incluso mejor moral, "¡Siempre haz una copia de seguridad de tus datos!".
Jon Bentley

Respuestas:

15

Creo que este es el problema: debería haber creado los directorios A, B, C ... Z. Si lo hizo, el mvcomando debería haber movido los archivos a esos directorios.

Pero si no, el mvcomando mueve los archivos a archivos con esos nombres, A, B, C ... y creo que esto es lo que hiciste.

Para que el shellscript sea más seguro, debe hacer que cree los directorios (si aún no están allí) antes de comenzar a moverlo.

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 desea que las cosas sean aún más seguras, también puede usar mvla -iopción

   -i, --interactive
          prompt before overwrite
sudodus
fuente
1
¿ touchAgregar sería un buen sustituto para mkdirevitar conflictos en caso de ejecutar el script varias veces?
Soy una calculadora TI el
2
touchcrea un archivo si el nombre no existe. Por lo tanto, no hará lo que quieras en este caso. mkdir -ppuede manejar el uso del script varias veces.
sudodus
66
Otra forma sencilla de hacer mvmás seguro es adquirir el hábito de agregar una barra diagonal al nombre del objetivo cuando el objetivo es un directorio, es decirmv "$file" "$dir/"
Steeldriver
7

@Sudodus ya explicó lo que salió mal, pero aquí hay una versión más simple de su script para la próxima vez:

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

Explicación

  • for letter in {a..z}; do: se {a..z}expande a todas las letras minúsculas entre ay 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

    Entonces esto iterará sobre todas las letras minúsculas, guardando cada una como $letter.

  • dir=${letter^}: la sintaxis ${var^^}devuelve el contenido de la variable $varcon el primer carácter en mayúscula (ya que este solo tiene un carácter, eso es todo lo que necesitamos). Entonces, si $letteres a, entonces ${letter^^}es A, y por $dirlo tanto será la versión en mayúscula de la actual $letter.

  • mkdir -p -- "$dir": crea el directorio. Si ya existe, no haga nada ( -p). El -- significa el final de las opciones , y es útil para proteger contra los nombres que comienzan con -.
  • mv -- "$letter"* "${letter^}"* "$dir" : mueve cada archivo (o directorio) al objetivo relevante.

El problema con esto es que también moverá cualquier directorio que pueda tener. No moverá los directorios de destino, porque aún no existen, o intentará moverlos dentro de sí mismos, pero cualquier directorio existente que no sea el directorio de destino se moverá.

Si eso es un problema, tendrá que hacer algo como esto:

for file in *; do 
    if [[ ! -d "$file" ]]; then 
        letter="${file:0:1}"
        dir="${letter^}"
        mkdir -p -- "$dir"
        mv -- "$file" "$dir"/
    fi
done
terdon
fuente
¿Cuál es la diferencia entre ${letter^}y ${letter^^}, y si son idénticos, por qué usar ${letter^^}en lugar de $dir?
Financia la demanda de Mónica el
1
@NicHartley ${var^}escribe en mayúscula solo la primera letra, mientras que ${var^^}escribe en mayúscula todas las letras. Aquí no hace ninguna diferencia, ya que $lettersolo tiene una letra.
terdon
Esta es una respuesta perfecta, excepto que es posible que desee agregar una capa adicional de cuidado agregando una barra diagonal $diren el mvcomando. (En su forma actual fallará si un archivo preexiste con un nombre en mayúscula de una letra)
Stig Hemmer
@StigHemmer, ¡sí! Muy buen punto, gracias. Respuesta editada.
terdon
4

En lugar de verificar cada archivo con una matriz de diccionario que crea una gran cantidad de iteraciones, puede hacer coincidir archivos con patrones.

Tipo muy básico:

#!/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
fuente
Tal vez lo hice mal, pero eso definitivamente no funcionó, perdí diez archivos jajaja. Estoy teniendo problemas para entender la parte de RESPUESTA? ¿Cuál es la intención? Todo lo que queda (dentro de las carpetas ABCD ...) son los propios archivos de alias que apuntan a .. tha alias
Soy una calculadora de TI
REPLY establecido en la línea de entrada leída por el comando de lectura incorporado cuando no se proporcionan argumentos.
Bac0n
ok y luego al final haces ln -rfst "$ sorted / $ label" - "$ REPLY" ¿por qué usar el alias si acabamos de moverlos con mv?
Soy una calculadora TI el
Por defecto se crea enlaces de directorio "vídeos" en el directorio ordenada, si quieres que te mv que anular el comentario "videos movimiento" y "videos de comentario Enlace" (creo que los enlaces simbólicos son menos miedo)
bac0n
... simplemente no ambos al mismo tiempo.
Bac0n
2

Salvaguardar en su .bashrc:

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

fuente
1
@ Zanna, ¡Gracias por eso! Citas agregadas.
¿Pocas preguntas solo para estar seguro (ser un novato), agregar el archivo .zshrc también es válido (si usa ZSH)? en man mv dice -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)que, ¿importa que la etiqueta -n esté antes de seguir las etiquetas? Los = --backup numeradas va a crear un doble de cada derecho, no es que un (terabytes hablando) poco exagerado (y la energía espacio / consumir) cuando se trata de archivos de vídeo súper-grande es. Gracias !
Soy una calculadora de TI el
2

Para el registro, algunas formas de evitar mvsobrescribir los archivos existentes:

  • Si desea moverse a un directorio, agregue una barra diagonal al objetivo, es decir, use en mv "$file" "$dir"/lugar de mv "$file" "$dir". Si $dirno existe o no es un directorio, mvse quejará:

    $ 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

    Esto parece hacer que el sistema llame rename("a", "z/"), por lo que debe estar a salvo de vulnerabilidades de tiempo de verificación a tiempo de uso, en caso de que alguien esté manejando el mismo conjunto de archivos al mismo tiempo.

  • Alternativamente, use mv -t "$dir" "$file". Nuevamente, se quejará si $dirno es un directorio.

  • Use la -nopción para evitar sobrescribir archivos existentes:

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

    No impedirá que cambie el nombre del primer archivo, pero no lo eliminará con los demás.

    Esto parece ser simple rename(), por lo que podría no ser seguro con el manejo simultáneo. (Hay renameat2()que admitiría una bandera para evitar sobrescribir).

ilkkachu
fuente
1

Si bien aparentemente no es el caso para usted, es posible que pueda hacer esto y no perder los archivos. Esto requeriría una de dos cosas para ser verdad:

  • Uno o más 'enlaces duros' a los mismos archivos existen en otra parte del sistema de archivos
  • Uno o más procesos tienen un archivo abierto

Los sistemas de archivos Unix permiten que más de una entrada de directorio haga referencia al mismo contenido de archivo . Esto se llama un ' enlace duro '. Puede crear enlaces duros con el lncomando, sin la -sopción común (suave / simbólica). Mientras exista al menos un enlace duro al contenido del archivo, el sistema de archivos no lo reutilizará.

(Nota al margen, los permisos generalmente se aplican al contenido del archivo, no a la entrada del directorio. Esta es la razón por la cual un usuario común a veces puede eliminar un archivo de su propiedad root, pero no escribir en él. La operación de eliminación modifica la carpeta, no el archivo en sí. )

El sistema de archivos tampoco reutilizará el contenido del archivo siempre que al menos un proceso tenga el archivo abierto. Incluso si no existe una entrada de directorio, el sistema de archivos no considerará que el espacio esté libre hasta que ningún proceso lo tenga abierto. El archivo puede recuperarse del sistema /proc/<pid>/fdde archivos virtual rootmientras el archivo permanezca abierto. (Gracias @fluffysheap).

fresa
fuente
1
Si se produce un proceso con el archivo abierto, puede recuperarlo buscándolo en / proc / <pid> / fd. Ver superuser.com/questions/283102/...
fluffysheap