Tenho uma pasta com mais de 500 vídeos e o tamanho da pasta é de cerca de 80 GB.
Quero mover 30 GB de vídeos para outra pasta, mas como?
Estou usando o terminal e não consigo instalar uma GUI.
Responder1
Não seletivamente (automaticamente)
Não conheço uma única ferramenta de linha de comando que possa fazer isso ... Mas você pode usar um script para mover arquivos até que um determinado tamanho total seja movido, por exemplo, assim (Execute o script apenas uma vez... Se você executá-lo novamente, ele moverá o tamanho especificado novamente dos arquivos restantes):
#!/bin/bash
sourc_dir="/path/to/directory" # Set the path to the source directory "no trailing /"
dest_dir="/path/to/directory" # Set the path to the destination directory "no trailing /"
move_size="30" # Set the total size of files to move in Gigabytes
# Don't edit below this line
move_size=$(( move_size * 1000000 ))
current_size=$(du -sk "$sourc_dir" | awk '{printf "%d", $1}')
remaining_size=$(( current_size - move_size ))
for f in "$sourc_dir"/*; do
[ -f "$f" ] || continue
new_size=$(du -sk "$sourc_dir" | awk '{printf "%d", $1}')
(( new_size <= remaining_size )) && exit
mv -n -- "$f" "$dest_dir"/
done
Ou até mesmo uma linha única (nenhum arquivo de script necessário) que você pode executar no terminal, por exemplo:
for f in /path/to/source/*; do [ -f "$f" ] || continue; (( size >= (30*(1000**3)) )) && break || (( size += $(stat --printf "%s" "$f") )) && mv -n -- "$f" /path/to/destination/; done
Você pode querer unset size
executá-lo novamente na mesma instância do shell.
Seletivamente(manualmente)
Você sempre pode especificar um padrão de shell ou canal em uma ferramenta de pesquisa com base em nomes de arquivos, tamanhos, tipos... etc.
Ou, mais convenientemente, você pode usar um gerenciador de arquivos baseado em console que suporta seleção múltipla de arquivos como, por exemplo, o conhecido e rico em recursosComandante da meia-noite GNUque está disponível nos repositórios oficiais do Ubuntu e pode ser instalado assim:
sudo apt install mc
e para executá-lo digite mc
e pressione Enter... Você será então apresentado a uma interface de usuário navegável onde poderá usar a Inserttecla do teclado para selecionar vários arquivos ... Você poderá ver o tamanho total dos arquivos selecionados e sua contagem em tempo real assim:
Depois de terminar de selecionar todos os arquivos que deseja mover, pressione a F6tecla do teclado para abrir a caixa de diálogo mover/renomear e entre no diretório de destino e escolha [< OK >]
mover os arquivos selecionados de uma vez, assim:
Ou mesmo o menos conhecido e com menos recursosaff (Gerenciador de arquivos muito rápido):-) ... Isso está interessantemente escrito em bash
... Embora suporte a seleção de vários arquivos, pode ser necessário personalizá-lo um pouco para poder ver o tamanho total dos arquivos selecionados, por exemplo, como um patch rápido, você pode alterar a linha 159
no arquivo de origem fff
deste:
local mark_ui="[${#marked_files[@]}] selected (${file_program[*]}) [p] ->"
para isso:
local mark_ui="[${#marked_files[@]}] $(for f in "${marked_files[@]}"; do (( tsize += $(stat --printf "%s" "$f") )); done; tsize=$((tsize/(1000**2))); printf '[%d M]' "$tsize";) selected (${file_program[*]}) [p] ->"
Então você pode simplesmente executar esse arquivo assim (Não é necessária instalação):
bash fff
Ou veja a ajuda sobre o uso assim:
bash fff -h
Então tudo que você precisa fazer é simplesmente navegar até o diretório de origem e começar a selecionar os arquivos para mover com o m(minúsculas) tecla do teclado assim:
Ao terminar de selecionar os arquivos, navegue até o diretório de destino e mova os arquivos previamente selecionados para ele pressionando o botão p(minúsculas) tecla do teclado.
Responder2
Aqui está uma alternativa paraRaffaé a ideia de que não requer um du
diretório completo para cada arquivo. Em vez disso, ele faz um stat
em cada arquivouma vez, usando find
's -printf
%s
para obter o tamanho e %p
o caminho para o arquivo a ser movido.
#!/bin/bash
src='foo'
dest='bar'
# 30GB in bytes to move:
(( bytestomove=30*(1000**3) ))
movedbytes=0
while IFS=$'\t' read -r -d '' bytes file
do
# if we already moved enough, break out
(( movedbytes >= bytestomove )) && break;
# try to move it, break out if it fails
mv -n -- "$file" "$dest" || break
# successfully moved, add bytes to movedbytes
(( movedbytes += bytes ))
done < <(find "$src" -type f -printf "%s\t%p\0")
(( movedmegs=movedbytes/(1000**2) ))
printf "Moved %d MB\n" $movedmegs
Responder3
@TedLyngmo apontou que a solução tar que apresentei pela primeira vez copia efetivamente os arquivos e não os move. Esta distinção é importante por 2 razões:
- Com uma cópia, os arquivos de origem permanecem, enquanto com uma movimentação, os arquivos de origem não existem mais em seus locais de origem.
- Se os arquivos forem movidos dentro do mesmo sistema de arquivos, como pode ser o caso nesta questão,
mv
simplesmente vinculará novamente o arquivo de origem ao novo local e não copiará o conteúdo. Normalmente, isso será significativamente mais rápido do que copiar, especialmente para arquivos grandes.
Mover cenário
Ainda podemos usar os tar
conceitos do cenário de cópia fornecido originalmente para construir uma lista de arquivos contendo apenas os primeiros 30 GB de arquivos encontrados tar
e, em seguida, mover apenas esses arquivos:
#!/bin/bash
# Convert human-readable number, e.g. 1M, 30G to number of 1024 byte blocks
limit=$(numfmt --from=auto $1 --to-unit 1024)
src_dir=$2
dest_dir=$3
mkdir $dest_dir
filelist=$(readlink -f $(mktemp -p $dest_dir))
# create a list of files to be moved. tar -v outputs filenames to stdout, and
# the dest archive is /dev/null
tar -v -L $limit -F false -c -f /dev/null -C $src_dir . > $filelist 2> /dev/null
tar_result=$?
if [ "$tar_result" = 2 ]; then
echo "$1 limit was hit - some unmoved files will remain in $dest_dir"
else
echo "All files will be moved"
fi
pushd $dest_dir > /dev/null
abs_dest_dir=$PWD
# create directories in the destination
grep '/.*/$' $filelist | xargs mkdir -p
popd > /dev/null
pushd $src_dir > /dev/null
# create hard links in the destination
grep -v '/$' $filelist | xargs cp -l --parents -t $abs_dest_dir
# unlink source files
grep -v '/$' $filelist | xargs rm
# remove source directories in reverse order, if they are empty
grep '/.*/$' $filelist | tac | xargs rmdir --ignore-fail-on-non-empty
popd > /dev/null
rm $filelist
Isso é mais complexo do que eu gostaria:
mv
não oferece tanto controle dos caminhos de origem e destino para vários arquivos (especificamente nenhum análogo dacp
opção --parents), portanto, as movimentações de arquivos são obtidas criando links físicos nos locais de destino e, em seguida, desvinculando os arquivos de origemComo os diretórios de origem contêm uma combinação de entradas que devem ou não ser movidas, os diretórios devem ser recriados no destino e não simplesmente movidos (junto com todo o conteúdo). Portanto, os diretórios de destino apropriados são criados antes de mover os arquivos, e os diretórios de origem correspondentes são removidos após a movimentação dos arquivos, se estiverem vazios.
Ressalvas
tar -v
produzirá uma entrada de arquivo por linha. Isto irá falhar espetacularmente se houver caracteres especiais nos nomes de caminho, por exemplo, novas linhas. Esperamos que os nomes dos caminhos de origem sejam nomeados de maneira sensata.Isso moverá um pouco menos de 30 GB de dados reais do arquivo, pois o fluxo contém metadados (nomes de arquivos, carimbos de data e hora, etc.), bem como o conteúdo real, e tudo isso está sujeito ao limite de 30 GB.
Estou jogando um pouco rápido e solto com KB vs. kb aqui. Como o
-L
parâmetro funciona em unidades de 1.024 bytes, isso limita a movimentação a 3.072.000.000 bytes de fluxo tar. Ajuste os números conforme apropriado.filelist.txt é criado no diretório atual. Serão necessárias permissões de gravação para isso, o que provavelmente é um dado adquirido, já que as operações de movimentação exigirão permissões de gravação de qualquer maneira.
Copiar cenário
Você pode usar tar
para transformar sua pasta em um fluxo e, em seguida, tar
novamente para transformar o fluxo novamente em arquivos em um novo local. O primeiro tar
limita -L
o "comprimento da fita" a 3.000.000 KB, antes de solicitar uma nova fita. tar
usa -F para executar false
após cada "fita". Porque false
retorna um erro, a transferência para após a primeira "fita", ou seja, 30GB. Fazer com que todos os dados passem por apenas 2 invocações de tar deve ser mais rápido do que fazer um loop em todos os arquivos com loops bash.
mkdir dest.dir
tar -cv -L 3000000 -F false -C src.dir . | tar -x -C dest.dir/
Isso copiará os arquivos até que o limite de 30 GB seja atingido e, em seguida, o tar
s apresentará um erro:
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now
tar: ‘false’ command failed
tar: Error is not recoverable: exiting now
tar
não transferirá o arquivo que estava sendo transferido quando o limite foi atingido - ele não será gravado no destino.
Ressalvas
Isso copiará um pouco menos de 30 GB de dados reais do arquivo, pois o fluxo contém metadados (nomes de arquivos, carimbos de data e hora, etc.), bem como o conteúdo real, e tudo isso está sujeito ao limite de 30 GB.
Estou jogando um pouco rápido e solto com KB vs. kb aqui. Como o
-L
parâmetro funciona em unidades de 1.024 bytes, isso limita a transferência a 3.072.000.000 bytes de fluxo tar. Ajuste os números conforme apropriado.
As edições anteriores desta resposta usaram find-cpio-head-cpio e depois tar-head-tar. A solução tar-tar agora apresentada deve ser suficiente, mas inspecione o histórico de edições se as outras soluções forem de interesse.
Responder4
Vá para a pasta onde estão seus arquivos:
cd SOURCE_FOLDER
faça uma lista de arquivos nessa pasta
ls > list.txt
Edite-o em qualquer editor e remova os arquivos que você NÃO deseja mover. Salve o resultado para que agora list.txt tenha apenas os arquivos que você deseja mover. Agora execute este script simples que lê cada linha do seu arquivo list.txt e move esse arquivo para o diretório de destino.
while IFS= read -r line; do mv "$line" ../DESTINATION_DIRECTORY/"$line"; done < list.txt