Preserva a estrutura de diretórios ao mover arquivos usando find

21

Eu criei o seguinte script que move arquivos antigos, conforme definido do diretório de origem para o diretório de destino. Está funcionando perfeitamente.

#!/bin/bash

echo "Enter Your Source Directory"
read soure

echo "Enter Your Destination Directory"
read destination 

echo "Enter Days"
read days



 find "$soure" -type f -mtime "-$days" -exec mv {} "$destination" \;

  echo "Files which were $days Days old moved from $soure to $destination"

Esse script move arquivos de maneira excelente. Ele também move arquivos do subdiretório de origem, mas não cria um subdiretório no diretório de destino. Quero implementar esse recurso adicional nele.

com o exemplo

/home/ketan     : source directory

/home/ketan/hex : source subdirectory

/home/maxi      : destination directory

Quando eu executo este script, ele também move os arquivos do hex no diretório maxi, mas eu preciso que o mesmo hex seja criado no diretório maxi e mova seus arquivos no mesmo hex lá.

    
por Ketan Patel 21.12.2012 / 15:26

9 respostas

10

Em vez de executar mv /home/ketan/hex/foo /home/maxi , você precisará variar o diretório de destino com base no caminho produzido por find . Isso é mais fácil se você mudar primeiro para o diretório de origem e executar find . . Agora você pode simplesmente preceder o diretório de destino para cada item produzido por find . Você precisará executar um shell no comando find … -exec para executar a concatenação e criar o diretório de destino, se necessário.

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  mkdir -p "$0/${1%/*}"
  mv "$1" "$0/$1"
' "$destination" {} \;

Observe que, para evitar problemas de cotação, se $destination contiver caracteres especiais, não será possível substituí-lo no script de shell. Você pode exportá-lo para o ambiente para que ele atinja o shell interno, ou você pode passá-lo como um argumento (foi o que eu fiz). Você pode economizar um pouco de tempo de execução agrupando sh chamadas:

destination=$(cd -- "$destination" && pwd) # make it an absolute path
cd -- "$source" &&
find . -type f -mtime "-$days" -exec sh -c '
  for x do
    mkdir -p "$0/${x%/*}"
    mv "$x" "$0/$x"
  done
' "$destination" {} +

Como alternativa, em zsh, você pode usar a zmv function e os qualificadores de glob . e m só correspondem aos arquivos regulares em o intervalo de datas certo. Você precisará passar uma função mv alternativa que primeiro crie o diretório de destino, se necessário.

autoload -U zmv
mkdir_mv () {
  mkdir -p -- $3:h
  mv -- $2 $3
}
zmv -Qw -p mkdir_mv $source/'**/*(.m-'$days')' '$destination/$1$2'
    
por 22.12.2012 / 00:58
12

Eu sei que find foi especificado, mas isso parece um trabalho para rsync .

Na maioria das vezes, uso o seguinte:

rsync -axuv --delete-after --progress Source/ Target/

Este é um bom exemplo se você quiser mover apenas arquivos de um tipo de arquivo específico ( aqui ):

rsync -rv --include '*/' --include '*.js' --exclude '*' --prune-empty-dirs Source/ Target/
    
por 18.09.2015 / 14:58
3

Não é tão eficiente, mas o código é mais fácil de ler e entender, na minha opinião, se você simplesmente copia os arquivos e os exclui depois.

find /original/file/path/* -mtime +7 -exec cp {} /new/file/path/ \;
find /original/file/path/* -mtime +7 -exec rm -rf {} \;

Aviso: falha descoberta pelo @MV para operações automatizadas:

Using two separate operations is risky. If some files become older than 7 days while the copy operation is done, they won't be copied but they will be deleted by the delete operation. For something being done manually once this may not be an issue, but for automated scripts this may lead to data loss

    
por 12.10.2014 / 20:53
2

você poderia fazer isso usando duas instâncias de find (1)

Existe sempre o cpio (1)

(cd "$soure" && find … | cpio -pdVmu "$destination")

Verifique os argumentos para o cpio. Os que eu dei

    
por 21.12.2012 / 17:15
1

Você pode fazer isso anexando o caminho absoluto do arquivo retornado por find ao seu caminho de destino:

find "$soure" -type f -mtime "-$days" -print0 | xargs -0 -I {} sh -c '
    file="{}"
    destination="'"$destination"'"
    mkdir -p "$destination/${file%/*}"
    mv "$file" "$destination/$file"'
    
por 21.12.2012 / 15:35
0

Melhor (mais rápido e sem consumir espaço de armazenamento fazendo cópia em vez de mover), também não é afetado pelos nomes dos arquivos se eles contiverem caracteres especiais em seus nomes:

export destination
find "$soure" -type f "-$days" -print0 | xargs -0 -n 10 bash -c '
for file in "$@"; do
  echo -n "Moving $file to $destination/"'dirname "$file"'" ... "
  mkdir -p "$destination"/'dirname "$file"'
  \mv -f "$file" "$destination"/'dirname "$file"'/ || echo " fail !" && echo "done."
done'

Ou mais rápido, movendo um monte de arquivos ao mesmo tempo para multi-CPU, usando o comando "paralelo":

echo "Move oldest $days files from $soure to $destination in parallel (each 10 files by "'parallel --number-of-cores'" jobs):"
function move_files {
  for file in "$@"; do
    echo -n "Moving $file to $destination/"'dirname "$file"'" ... "
    mkdir -p "$destination"/'dirname "$file"'
    \mv -f "$file" "$destination"/'dirname "$file"'/ || echo " fail !" && echo "done."
  done
}
export -f move_files
export destination
find "$soure" -type f "-$days" -print0 | parallel -0 -n 10 move_files

P.S .: Você tem um erro de digitação, "soure" deve ser "fonte". Eu mantive o nome da variável.

    
por 16.08.2016 / 13:17
0

Como não parece haver uma solução realmente fácil para isso e eu preciso disso com muita frequência, criei esse utilitário de código-fonte aberto para o Linux (requer python): link

Existem várias maneiras de usá-lo para conseguir o que você precisa, mas provavelmente o mais simples seria algo assim:

 # -vf = verbose + force (doesn't stop on errors)
smv -vf 'find some_folder -type f -mtime "-$days"' target_folder

Você também pode executá-lo no modo dry para que ele não faça nada além de imprimir o que faria

smv -rvf 'find some_folder -type f -mtime "-$days"' target_folder

Ou caso essa lista de arquivos seja muito longa para caber na linha de argumento e você não se importa de executar python para cada arquivo, então

find "$soure" -type f -mtime "-$days" -exec smv {} "$destination" \;

Espero que isso ajude alguém

    
por 21.09.2016 / 16:01
0

Isso é menos elegante, mas fácil, se o número / tamanho dos arquivos não for muito grande

Compacte seus arquivos em um arquivo zip e descompacte no destino sem a opção -j . Por padrão, o zip criará a estrutura de diretórios relativa.

    
por 12.12.2017 / 19:03
0

Tente desta maneira:

IFS=$'\n'
for f in 'find "$soure" -type f -mtime "-$days"';
do
  mkdir -p "$destination"/'dirname $f';
  mv $f "$destination"/'dirname $f';
done
    
por 26.01.2018 / 09:18