Como extrair uma pasta especificamente nomeada de um diretório recursivo, excluir os outros?

10

Eu percebo que o título é vago, melhor explicado com um exemplo.
Eu tenho este tipo de árvore de diretórios que é obtida a partir da descompactação de um arquivo zip:

x -> y -> z -> run -> FILES AND DIRECTORIES HERE

Portanto, existem 4 diretórios, dos quais 3 são vazios de arquivos (x, y, z) e contêm apenas 1 subdiretório, e existe o diretório no qual estou interessado, chamado " run ".

Eu quero mover o diretório "run" em si (incluindo tudo dentro) para o local "raiz" onde descompactei (ou seja, onde "x" é, mas não dentro de "x").

Suposições: Existe existe uma pasta chamada "run", mas eu não sei quantos diretórios eu terei para "cd" para chegar a ele (poderia ser 3 (x, y, z ), pode ser 10 ou mais. Os nomes também são desconhecidos e não precisam ser x, y, z etc).

Como posso conseguir isso? Eu tentei muitas variações de esta questão mas todos falharam.

    
por Idos 05.03.2017 / 09:58

4 respostas

13

e

  find . -type d -name run -exec mv {} /path/to/X \;

onde

  • / caminho / para / X é o seu diretório de destino
  • você começa neste mesmo lugar.
  • use outra resposta para remover diretórios vazios.

(em uma nota lateral há uma opção --junk-paths no zip, seja ao compactar ou descompactar)

    
por 05.03.2017 / 10:12
7

Eu faria isso em bash , usando globstar . Conforme explicado em man bash :

globstar
      If set, the pattern ** used in a pathname expansion con‐
      text will match all files and zero or  more  directories
      and  subdirectories.  If the pattern is followed by a /,
      only directories and subdirectories match.

Portanto, para mover o diretório run para o diretório de nível superior x e, em seguida, exclua o restante, você pode fazer:

shopt -s globstar; mv x/**/run/ x/  && find x/ -type d -empty -delete

O comando shopt ativa a opção globstar . O mv x/**/run/ x/ moverá todos os subdiretórios denominados run (observe que isso só funcionará se houver apenas um diretório run ) para x e o find excluirá todos os diretórios vazios.

Você poderia fazer a coisa toda no shell com globbing estendido, se preferir, mas eu prefiro a rede de segurança de find -empty para ter certeza de que nenhum diretório não vazio seja deletado. Se você não se importa com isso, você pode usar:

shopt -s globstar; shopt -s extglob; mv x/**/run/ x/ && rm -rf x/!(run)
    
por 05.03.2017 / 15:09
6

Definitivamente mais detalhado, mas fazendo o trabalho em uma etapa:

Supondo que você tenha o python instalado

#!/usr/bin/env python3
import shutil
import os
import sys

dr = sys.argv[1]

for root, dirs, files in os.walk(dr):
    # find your folder "run"
    for pth in [d for d in dirs if d == sys.argv[2]]:
        # move the folder to the root directory
        shutil.move(os.path.join(root, pth), os.path.join(dr, pth))
        # remove the folder (top of the tree) from the directory
        shutil.rmtree(os.path.join(dr, root.replace(dr, "").split("/")[1]))

Como usar

  1. Copie o script em um arquivo vazio, salve-o como get_folder.py
  2. Execute-o com o diretório raiz (contendo seu material descompactado) e o nome da pasta para "levantar" como argumentos:

    python3 /full/path/to/get_folder.py /full/path/to/folder_containing_unzipped_dir run
    

E está feito:

    
por 05.03.2017 / 11:51
4

Solução Perl

#!/usr/bin/env perl
use strict;
use warnings;
use File::Find;
use File::Copy::Recursive qw(dirmove);

my @wanted;
find(sub{ -d $_ && $_ eq "run" && push @wanted,$File::Find::name}, $ARGV[0]);
dirmove("$wanted[0]","./run");

A maneira como isso funciona é simples: find() sub-rotina percorrerá uma determinada árvore de diretórios recursivamente, localizará o diretório cujo nome é run e o colocará em um array de nomes de arquivos desejados. Nesse caso, supomos que deve haver apenas um arquivo na lista, assim, quando o comando for concluído, usaremos dirmove() do módulo File::Copy::Recursive e movê-lo para o diretório de trabalho atual do qual o script foi chamado. Assim, no seu caso, você poderia chamá-lo do diretório pai para x .

Árvore de diretórios original:

$ tree                                                                                                                   
.
└── levelone
    └── leveltwo
        └── levelthree
            └── run
                ├── file1
                ├── file2
                └── file3

Resultado:

$ ~/find_dir.pl .  
$ tree 
.
├── levelone
│   └── leveltwo
│       └── levelthree
└── run
    ├── file1
    ├── file2
    └── file3
    
por 06.03.2017 / 00:19