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.