Diretório pai-filho com o mesmo nome, mova os arquivos para o diretório pai

Diretório pai-filho com o mesmo nome, mova os arquivos para o diretório pai

Preciso encontrar uma maneira de pesquisar diretórios filhos com o mesmo nome e, em seguida, mover todos os arquivos do diretório filho para o pai. Assim, /recup-dir1/recup-dir1/files to /recup-dir1/files. os diretórios filhos podem ser deixados vazios porque posso usar algo como find . -type -d -empty -delete excluir todos os diretórios vazios

Portanto, o problema é que não tenho ideia de quais diretórios existem os diretórios filhos com o mesmo nome e quais não existem.

Em pseudocódigo eu preciso de algo assim.

While more directories are unchecked
get name-x of  next dir
   enter dir  
   If name-x/name-x exist
   move all files in name-x/name-x to name-x
   mark dir as done
next 

Meu melhor palpite é criar um pequeno script python para fazer uma lista de todos os diretórios que possuem um filho com o mesmo nome e fazer um loop nessa lista por meio de um comando como find something something -exec mv

Talvez isso possa ser feito com scripts bash ou exista outra solução. Como alguns comandos rsync, porém, como criei essa bagunça provavelmente com o rsync, não acho que essa seja a solução.

Editar: aqui está uma parte real da saída da árvore: Os diretórios de nível superior estão dentro de /mnt/external-disk/tst-backup Não há subdiretórios em níveis inferiores.

│   └── recup_dir.1
├── recup_dir.10
│   └── recup_dir.10
├── recup_dir.100
│   └── recup_dir.100
├── recup_dir.102
│   └── recup_dir.102
└── recup_dir.1020
    └── recup_dir.1020

Responder1

Com zshvocê poderia fazer:

for dir in **/*(NDodoN/e['[[ $REPLY:t = $REPLY:h:t ]]']); do
  contents=($dir/*(NDoN))
  (( $#contents == 0 )) ||
    mv -- $contents $dir:h/ &&
    rmdir -- $dir
done

Onde:

  • **/*(qualifiers)globbing recursivo com qualificadores glob
  • N: nullglob: não reclame se não houver correspondência
  • D: dotglob: inclui arquivos ocultos
  • od: ordene a profundidade primeiro (folhas antes dos galhos em que estão).
  • oN: caso contrário, não se preocupe em ordenar a lista de arquivos.
  • /: restringe a arquivos do tipo diretório.
  • e['expression']: restringe aos arquivos para os quais o expressioncódigo retorna verdadeiro (dentro dos quais o caminho do arquivo atual está armazenado $REPLY).
  • $REPLY:t: tail (nome base) do arquivo
  • $REPLY:h:t: cauda da cabeça (dirname) dos arquivos)

Com bash4.4+ e GNU findou a findmaioria dos BSDs, você poderia fazer algo semelhante com:

shopt -s nullglob dotglob
readarray -td '' dirs < <(
  LC_ALL=C find . -depth -regex '.*\(/[^/]*\)\1' -type d -print0
)
for d in "${dirs[@]}"; do
  contents=("$d"/*)
  (( ${#contents[@]} == 0 )) ||
    mv -- "${contents[@]}" "${d%/*}/" &&
    rmdir -- "$d"
done

Desta vez, usando uma expressão regular para corresponder aos ./path/to/dir/dirarquivos usando referências anteriores de expressões regulares básicas.

Responder2

Tente isto, baseado em GNU findv4.8.0 e Bash v5.1.8

Parte 1: Analisar árvore de diretórios + detectar duplicatas de nomes de subdiretórios

Suponha que um determinado diretório em sua árvore tenha a seguinte estrutura:

./
|__test1/
     |__dirname with space
     |           |__test2
     |                |__ test2
     |__dirname **
     |       |__test1
     |
     |__reboot
     |     |__test1
     | 
     |__test2/
          |__test3/
               |__test2/
                    |__test1/
                         |__test1/

(Nomes de diretórios estranhos existem para demonstrar a segurança do código.)

Você vê que alguns subdiretórios (subdiretórios) são repetidos de maneiras diferentes. Alguns são repetidos várias vezes, não apenas uma vez (por exemplo, test1), um não é repetido ( test3) e podem ser repetidos como pai e filho ou separados por um número arbitrário de subdiretórios intermediários.

O código abaixo revela nomes de subdiretórios ingênuos em uma estrutura de diretórios de maneira detalhada.

  • ele analisa a árvore de arquivos para a estrutura do subdiretório começando em$PWD
  • ele encontra duplicatas para cada componente de qualquer caminho de subdiretório de 2 ou mais níveis, sem contar o nível raiz que é $PWD. Na minha experiência, o caminho de subdiretório mais longo é: ./test1/test2/test1/test3/test2/test1/test1
  • ele imprime o primeiro subdiretório encontrado em cada nível de subdiretório, começando na folha, ou seja, lendo o caminho do subdiretório da direita para a esquerda.
  • a impressão é redirecionada para um arquivo, na ordem inversa, de modo que o caminho mais longo do subdiretório seja mostrado primeiro. Dois pontos e vírgulas consecutivos separam os componentes do caminho (à esquerda de ";;"), do primeiro dupe (à direita de ";;") encontrado de acordo com o marcador anterior.

[Código]

$ find ./* -type d -exec bash -c 'set -o noglob; IFS="/" subdir=($(printf "%s " "$1")); dirlevels=$((${#subdir[@]}-1)); dupe="$(awk '\''!($1 in sd) {sd[$1];next} {print $1}'\'' < <(printf "%s\n" ${subdir[@]:1}))";[ $dirlevels -ge 2 ] && [ ! -z "$dupe"  ] && (printf "%s/" "${subdir[@]:1}";printf " ;; %s\n" "$(tail -n 1 < <(printf "%s\n" "$dupe"))";)' shellexec {} \; | tac >| tmp.data

$ cat -n  tmp.data

1 test1/reboot/test1/ ;; test1
2 test1/dirname with space/test2/test2/ ;; test2
3 test1/test2/test1/test3/test2/test1/test1/ ;; test1
4 test1/test2/test1/test3/test2/test1/ ;; test1
5 test1/test2/test1/test3/test2/ ;; test2
6 test1/test2/test1/test3/ ;; test1
7 test1/test2/test1/ ;; test1
8 test1/dirname **/test1/ ;; test1

Parte 2: Processamento de duplicatas de nomes de subdiretórios; mover conteúdo

O processamento ocorre na ordem exibida em tmp.data.

  • on tmp.datana primeira linha, o primeiro nome enganado no caminho ./test1/test2/test1/test3/test2/test1/test1é test1. Podemos transferir seu conteúdo para o nível do subdiretório mais à esquerda com o mesmo nome:./test1/
  • uma vez que o conteúdo tenha sido movido sem destruir os arquivos existentes no destino, o nível de subdiretório mais à direita test1é excluído.
  • passamos para a linha 2 tmp.datae repetimos as etapas acima.
  • etc até que todas as linhas tmp.datatenham sido consumidas.

Neste estágio, a pergunta (para o autor da pergunta: @TomDerks) é o que fazer com o que está mais à direita test1/*na linha 6? Devetodosseu conteúdo seja movido para o diretório mais à esquerda com o mesmo nome, que neste caso é o primeiro nível de subdiretório no caminho? "todos" inclui arquivos em./test1/test2/test1/ assim comoo subdiretório test3e seu conteúdo?
A solução completa (Parte 2) depende disso.

informação relacionada