Achatando um diretório aninhado

56

Isso é provavelmente muito simples, mas não consigo entender. Eu tenho uma estrutura de diretório como esta (dir2 está dentro dir1):

/dir1
    /dir2 
       |
        --- file1
       |
        --- file2

Qual é a melhor maneira de 'achatar' essa estrutura do diretor de tal forma que o arquivo1 e o arquivo2 sejam colocados em dir1 e não em dir2.

    
por turtle 24.10.2012 / 21:28

4 respostas

60

Você pode fazer isso com o GNU find e o GNU mv :

find /dir1 -mindepth 2 -type f -exec mv -t /dir1 -i '{}' +

Basicamente, a maneira que funciona se esse find passar pela árvore de diretórios inteira e por cada arquivo ( -type f ) que não esteja no diretório de nível superior ( -mindepth 2 ), executará um mv para movê-lo para o diretório desejado ( -exec mv … + ). O argumento -t para mv permite especificar primeiro o diretório de destino, o que é necessário porque a forma + de -exec coloca todos os locais de origem no final do comando.

Como Stephane Chazelas aponta, o acima só funciona com ferramentas GNU (que são padrão no Linux, mas não na maioria dos outros sistemas). O seguinte é um pouco mais lento (porque invoca mv várias vezes), mas muito mais universal:

find /dir1 -mindepth 2 -type f -exec mv -i '{}' /dir1 ';'
    
por 24.10.2012 / 21:30
26

No zsh:

mv dir1/*/**/*(.D) dir1

**/ atravessa subdiretórios recursivamente. O qualificador de glob . corresponde apenas aos arquivos regulares, e D garante esse ponto os arquivos estão incluídos (por padrão, os arquivos cujo nome começa com . são excluídos das correspondências curinga). Para limpar os diretórios agora vazios, execute rmdir dir1/**/*(/Dod) - / restringe aos diretórios e od ordena primeiro a profundidade das correspondências para remover dir1/dir2/dir3 antes de dir1/dir2 .

Se o tamanho total dos nomes dos arquivos for muito grande, você poderá ter uma limitação no comprimento da linha de comando. O Zsh construiu para mv e rmdir que não são afetados por esta limitação: execute zmodload zsh/files para habilitá-los.

Com apenas ferramentas POSIX:

find dir1 -type f -exec mv {} dir1 \;
find dir1 -depth -exec rmdir {} \;

ou (mais rápido porque não precisa executar um processo separado para cada arquivo)

find dir1 -type f -exec sh -c 'mv "$@" dir1' _ {} +
find dir1 -depth -exec rmdir {} +
    
por 25.10.2012 / 02:01
3

Tente fazer isso:

cp /dir1/dir2/file{1,2} /another/place

ou para cada arquivo correspondente a file[0-9]* no subdir:

cp /dir1/dir2/file[0-9]* /another/place

Veja o link

    
por 24.10.2012 / 21:30
1

Eu escrevi duas funções que você pode usar juntas para fazer isso, você pode limitar o nível do diretório adicionando um parâmetro -maxdepth $VAL .

# This scripts flattens the file directory
# Run this script with a folder as parameter:
# $ path/to/script path/to/folder

#!/bin/bash

rmEmptyDirs(){
    local DIR="$1"
    for dir in "$DIR"/*/
    do
        [ -d "${dir}" ] || continue # if not a directory, skip
        dir=${dir%*/}
        if [ "$(ls -A "$dir")" ]; then
            rmEmptyDirs "$dir"
        else
            rmdir "$dir"
        fi
    done
    if [ "$(ls -A "$DIR")" ]; then
        rmEmptyDirs "$DIR"
    fi
}

flattenDir(){
    local DIR="$1"
    find "$DIR" -mindepth 2 -type f -exec mv -i '{}' "$DIR" ';'
}

read -p "Do you wish to flatten folder: ${1}? " -n 1 -r
echo    # (optional) move to a new line
if [[ $REPLY =~ ^[Yy]$ ]]
then
    flattenDir "$1" &
    rmEmptyDirs "$1" &
    echo "Done";
fi
    
por 25.07.2016 / 03:28