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 zsh
você 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 globN
: nullglob: não reclame se não houver correspondênciaD
: dotglob: inclui arquivos ocultosod
: 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 oexpression
có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 bash
4.4+ e GNU find
ou a find
maioria 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/dir
arquivos usando referências anteriores de expressões regulares básicas.
Responder2
Tente isto, baseado em GNU find
v4.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.data
na 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.data
e repetimos as etapas acima. - etc até que todas as linhas
tmp.data
tenham 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 test3
e seu conteúdo?
A solução completa (Parte 2) depende disso.