Bash LINUX - Criar recursivamente diretórios intermediários e mover arquivos de tipo para novos diretórios

0

Eu usei o StackExchange antes e espreitei no SuperUser, mas na primeira vez que postei. Aqui está o que estou tentando fazer. (Pseudo)

For Each DIR containing $file *.Item
    Mkdir ./dir1/dir2
    Mv $file ./dir1/dir2/$file

Basicamente eu tenho uma estrutura de pastas cheia de arquivos do tipo .Item, em todo diretório contendo um arquivo do tipo .Item eu quero criar 2 novos sub-diretórios chamá-los dir1 / dir2 / então eu quero mover todos os arquivos do tipo .Item nos subdiretórios.

Até agora eu tive sorte fazendo algumas das partes disso, mas não todas ao mesmo tempo. Peço desculpas se isso já foi respondido.

    
por Asterdahl 09.08.2014 / 01:15

1 resposta

0

O principal problema aqui é evitar mover os arquivos mais de uma vez. Um loop ingênuo corre o risco de encontrar dir/x.Item , movê-lo para dir/dir1/dir2/x.Item , e então encontrar dir/dir1/dir2/x.Item e movê-lo para dir/dir1/dir2/dir1/dir2/x.Item , e então ...

Embora eu não conseguisse fazer o Gnu find fazer isso em alguns minutos de teste, eu noto que o Posix diz "Se um arquivo é removido ou adicionado à hierarquia de diretórios que está sendo pesquisada, não é especificado se ou não find inclui esse arquivo em sua pesquisa. " Então, um deve ser defensivo.

Portanto, a solução mais fácil é primeiro construir a lista de arquivos e depois processar a lista.

Se nenhum arquivo .Item tiver um caractere de nova linha em seu nome, será fácil usar find para construir a lista. Supondo que não haja muitos arquivos, isso pode ser feito diretamente em uma matriz bash:

mapfile -t files < <(find . -name '*.Item')

Em seguida, é simples processar os arquivos:

for f in "${files[@]}"; do
  # make the target directory if it doesn't yet exist (-p)
  mkdir -p "$(dirname "$f")"/dir1/dir2
  # move the file
  mv "$f" "$(dirname "$f")/dir1/dir2
done

Se houver muitos arquivos, precisamos ser mais eficientes e menos arrogantes sobre a memória. Podemos construir uma lista de diretórios em vez de uma lista de arquivos ( sort -u elimina duplicatas):

mapfile -t dirs < <(find . -name '*.Item' -printf %h | sort -u)

Agora podemos processar os diretórios:

for d in "${dirs[@]}"; do
  mkdir -p "$d/dir1/dir2"
  mv "$d/*.Item" "$d/dir1/dir2"
done

Por fim, se houver muitos diretórios, poderemos usar um arquivo temporário em vez de um array:

tmp=$(mktemp)
find . -name '*.Item' -printf %h | sort -u > "$tmp"
while IFS= read -r dir; do
  mkdir -p "$d/dir1/dir2"
  mv "$d/*.Item" "$d/dir1/dir2"
done < "$tmp"
rm "$tmp"
    
por 09.08.2014 / 09:09

Tags