Arquivos 'sobrescritos', espaço ainda ocupado, eles estão perdidos?


11

Por isso, muitíssimo impaciente, usei o seguinte script no meu servidor 19.04, na tentativa de mover vários arquivos de vídeo para pastas com prefixos:

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

Nenhuma pista de onde deu errado, mas, em vez de mover os arquivos para as pastas, foi para uma saída singular .. então:

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

Felizmente, parei o processo (CTRL + C) assim que percebi que não estava indo como o esperado e não passei por toda a pasta.

Então agora eu tenho esses arquivos Ae C, que são menores que um Gb, e pela aparência dele é um vídeo ÚNICO.

Existem 50 GB não contabilizados no uso total do disco da própria pasta, mas o espaço total em disco do computador permaneceu o mesmo. Me fazendo pensar que os arquivos não foram excluídos?

Qualquer ajuda apreciada, obrigado :)

Editar: os arquivos acabaram, apenas o último arquivo a ser gravado permanece, bastou algum tempo para que as informações de uso do disco fossem atualizadas .. moral da história, execute seus scripts em arquivos simulados antes!


3
E que os diretórios chamado A, Be assim por diante existia antes de executar o script? Caso contrário, você apenas renomeou os arquivos. Todos os arquivos cujos nomes começaram aou Aforam renomeados A, portanto, apenas o último arquivo renomeado sobreviveu, os outros são substituídos. Chamar uma variável dirnão cria um diretório!
— precisa saber é o seguinte

2
é assim que ele interpreta também. "último arquivo renomeado sobrevivido" ha. diretórios não existiam, eu deveria ter adicionado um 'toque' para cada um antes da mão. obrigado por esclarecer
— eu sou uma calculadora TI

4
+1 em ".. moral da história, execute seus scripts em arquivos simulados antes!"
— sudodus 14/09/19

4
Uma dica para evitar problemas como este: Use mv "$file" "$dir/", com um final /; se $dirnão existir, mvocorrerá um erro em vez de renomear $filepara $dir. Considere também mv -ie mv -n. E sempre faça um mkdir -pantes de mover, para uma boa medida.
— marcelm

3
@sudodus Ainda melhor, "Sempre faça backup dos seus dados!".
— 21419 Jon

Respostas:


15

Eu acho que esse é o problema: você deveria ter criado os diretórios A, B, C ... Z. Se criou, o mvcomando deveria ter movido os arquivos para esses diretórios.

Mas se não, o mvcomando move os arquivos para arquivos com esses nomes, A, B, C ... e acho que foi isso que você fez.

Para tornar o shellscript mais seguro, você deve criar os diretórios (se eles ainda não estiverem lá) antes de iniciar a mudança.

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 quiser que as coisas fiquem ainda mais seguras, você também pode usar mvcom a -iopção

   -i, --interactive
          prompt before overwrite

1
adicionar touchseria um bom substituto para mkdirevitar conflitos no caso de executar o script várias vezes?
— Eu sou uma calculadora de TI

2
touchcria um arquivo se o nome não existir. Portanto, não fará o que você deseja neste caso. mkdir -ppode lidar com o uso do script várias vezes.
— sudodus 14/09/19

6
Outra forma simples você pode fazer mvmais seguro é fazer com que o hábito de adicionar uma barra final para o nome de destino quando o alvo é um diretório ou sejamv "$file" "$dir/"
— steeldriver

7

O @Sudodus já explicou o que deu errado, mas aqui está uma versão mais simples do seu script da próxima vez:

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

Explicação

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

    Portanto, isso irá iterar todas as letras minúsculas, salvando cada uma como $letter.

  • dir=${letter^}: a sintaxe ${var^^}retorna o conteúdo da variável $varcom o primeiro caractere em maiúsculas (já que este possui apenas um caractere, é tudo o que precisamos). Então, se $letteré a, então ${letter^^}é Ae, portanto $dir, será a versão em maiúscula da corrente $letter.

  • mkdir -p -- "$dir": crie o diretório Se já existir, não faça nada ( -p). A -- significa o fim das opções , e é útil para proteger contra os nomes que começam com -.
  • mv -- "$letter"* "${letter^}"* "$dir" : mova todos os arquivos (ou diretórios) para o destino relevante.

O problema é que ele também moverá os diretórios que você tiver. Ele não moverá os diretórios de destino, porque eles ainda não existem ou você tentará movê-los para eles mesmos, mas quaisquer diretórios existentes que não sejam o diretório de destino serão movidos.

Se isso for um problema, você terá que fazer algo assim:

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

Qual é a diferença entre ${letter^}e ${letter^^}, e se eles são idênticos, por que usar ${letter^^}no lugar de $dir?
— Fund Monica's Lawsuit

1
O @NicHartley coloca em ${var^}maiúscula apenas a primeira letra, enquanto ${var^^}todas as letras em maiúscula. Não faz nenhuma diferença aqui, pois $lettersó tem uma letra.
— terdon 16/09/19

Essa é uma resposta perfeita, exceto que você pode querer adicionar uma camada extra de cuidado adicionando uma barra $dirno diretório mv. (Na sua forma actual ele irá falhar se um preexiste arquivo com um nome maiúscula de uma única letra)
— Stig Hemmer

@StigHemmer grita, sim, de fato. Muito bom ponto, obrigado. Resposta editada.
— terdon 16/09/19

4

Em vez de verificar todos os arquivos em uma matriz de dicionário que cria muita iteração, você pode comparar arquivos com padrões.

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

Talvez eu tenha feito errado, mas isso definitivamente não funcionou, perdi dez arquivos lol. Estou tendo problemas em subestimar a parte da resposta? qual é a intenção Tudo o que resta (dentro das pastas ABCD ....) são os próprios arquivos de aliases apontando para .. tha alias
— Eu sou uma calculadora de TI

REPLY definido como a linha de entrada lida pelo comando read builtin quando nenhum argumento é fornecido.
— bac0n 14/09/19

ok e então no final você faz ln -rfst "$ classificado / $ label" - "$ REPLY" por que o alias se nós apenas os movemos com mv?
— Eu sou uma calculadora de TI

Como padrão, ele cria links de diretório "vídeos" em seu diretório ordenada, se você quiser mv-los você precisa descomentar "vídeos Move" e comentar "vídeos Link" (eu acho links simbólicos são menos assustador)
— bac0n

... apenas não os dois ao mesmo tempo.
— bac0n

2

Proteja seu .bashrc:

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

1
@ Zanna, obrigado por isso! Citações adicionadas.

Poucas perguntas, apenas para ter certeza (sendo um novato), adicionar o arquivo .zshrc também é válido (se você estiver usando o ZSH)? no man mv diz -n Do not overwrite an existing file. (The -n option overrides any previous -f or -i options.)isso, importa que a tag -n seja antes de seguir as tags? O --backup = numerado criará o dobro de cada direito, não é um exagero (e energia / consumo de espaço) ao lidar com arquivos de vídeo super grandes (terabytes em conversação). Obrigado !
— Eu sou uma calculadora de TI

2

Para o registro, algumas maneiras de impedir a mvsubstituição de arquivos existentes:

  • Se você deseja mover para um diretório, adicione uma barra ao destino, ou seja, use em mv "$file" "$dir"/vez de mv "$file" "$dir". Se $dirnão existir ou não for um diretório, você mvirá reclamar:

    $ 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

    Isso parece fazer a chamada do sistema rename("a", "z/"), portanto, ela deve estar protegida das vulnerabilidades do tempo de verificação ao tempo de uso, caso alguém esteja manipulando o mesmo conjunto de arquivos ao mesmo tempo.

  • Como alternativa, use mv -t "$dir" "$file". Novamente, ele reclamará se $dirnão for um diretório.

  • Use a -nopção para evitar a substituição de arquivos existentes:

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

    Não impedirá que renomeie o primeiro arquivo, mas depois não o descartará com os outros.

    Isso parece ser simples rename(), portanto pode não ser seguro com o manuseio simultâneo. (Existe renameat2()um suporte para um sinalizador para evitar a substituição.)


1

Embora aparentemente não seja o seu caso, é possível que você possa fazer isso e não perder os arquivos. Isso exigiria que uma das duas coisas fosse verdadeira:

  • Um ou mais 'links físicos' para os mesmos arquivos existem em outras partes do sistema de arquivos
  • Um ou mais processos tem um arquivo aberto

Os sistemas de arquivos Unix permitem que mais de uma entrada de diretório se refira exatamente ao mesmo conteúdo de arquivo . Isso é chamado de ' link físico '. Você pode criar links físicos com o lncomando, sem a opção comum -s(suave / simbólica). Enquanto existir pelo menos um link físico para o conteúdo do arquivo, ele não será reutilizado pelo sistema de arquivos.

(Observação: as permissões geralmente se aplicam ao conteúdo do arquivo, não à entrada do diretório. É por isso que um usuário comum às vezes pode excluir um arquivo pertencente a root, mas não gravar nele. A operação de exclusão modifica a pasta, não o próprio arquivo. )

O sistema de arquivos também não reutilizará o conteúdo do arquivo, desde que pelo menos um processo tenha o arquivo aberto. Mesmo que não exista uma entrada no diretório, o sistema de arquivos não considerará o espaço livre até que nenhum processo o abra. O arquivo pode ser recuperado do sistema /proc/<pid>/fdde arquivos virtual rootenquanto o arquivo permanecer aberto. (Obrigado @fluffysheap.)


1
Se houver um processo com o arquivo aberto, você poderá recuperá-lo em / proc / <pid> / fd. Veja superuser.com/questions/283102/…
— fluffysheap
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.