Achatando a estrutura da pasta

8

Eu tenho esta estrutura de pastas:

├── foo1
   ├── bar1.txt
   └── bar2.txt
├── foo2
   ├── bar3.txt
   └── bar4 with a space.txt
└── foo3
    └── qux1
        ├── bar5.txt
        └── bar6.txt

que eu gostaria de simplificar, com um sublinhado entre cada nível de pasta:

├── foo1_bar1.txt
├── foo1_bar2.txt
├── foo2_bar3.txt
├── foo2_bar4 with a space.txt
├── foo3_qux1.bar6.txt
└── foo3_qux1_bar5.txt

Eu olhei em volta e não encontrei nenhuma solução que funcione, principalmente porque o meu problema tem duas particularidades: pode haver mais de um nível de pasta dentro da raiz e também porque alguns arquivos podem ter espaços.

Alguma idéia de como fazer isso no bash? Obrigado!

Edit : Executando resposta proposta gleen jackman eu recebo isso:

insira a descrição da imagem aqui

Existem dois sublinhados para a pasta de primeiro nível. Alguma idéia de como evitar isso ou apenas renomeá-lo para que seja apenas um sublinhado? Obrigado.

nunos
fonte

Respostas:

15
find */ -type f -exec bash -c 'file=${1#./}; echo mv "$file" "${file//\//_}"' _ '{}' \;

remova echose estiver satisfeito de que está funcionando. Não se preocupe, pois os comandos repetidos não mostram aspas, o script manipulará arquivos com espaços corretamente.

Se você deseja remover os subdiretórios agora vazios:

find */ -depth -type d -exec echo rmdir '{}' \; 
Glenn Jackman
fonte
Ei. Fantástico! Obrigado. Isso fez o truque quase inteiramente. Só estou tendo um problema menor, que estou obtendo foo1__qux1_bar5.txt, dois sublinhados no primeiro nível de pasta. Tentei mexer com sua linha do bash, mas nada parecia funcionar. Talvez eu precise adicionar algo?
Nunos
@nunos, eu não posso reproduzir esse comportamento
glenn jackman
Mesmo? Esquisito. Acabei de atualizar minha resposta com mais algumas informações. Você pode dar uma olhada? Obrigado.
Nunos
1
Hã. OK, mude "${file//\//_}"para "${file//+(/)/_, e se este for um script,shopt -s extglob
glenn jackman
Eu também tive o problema com os dois sublinhados. Infelizmente, alterar o comando como você sugeriu não funcionou para mim. No entanto, encontrei esta resposta no AskDifferent: apple.stackexchange.com/a/254224/122152 De acordo com a resposta, o macOS pode estar usando o BSD find. Uma solução alternativa pode ser usar findutils. Ele pode ser instalado usando o homebrew com brew install findutilse, em seguida, usando em gfindvez de find. Isso funcionou para mim.
intagli
5

Usando a renomeação do perl:

find . -depth -type f -exec rename 's@(?<!\.)/@_@g' -- {} \;

RESULTADO

$ find -type f
./foo2_bar4 whit a space.txt
./foo3_qux1_bar6.txt
./foo2_bar3.txt
./foo3_qux1_bar5.txt
./foo1_bar2.txt
./foo1_bar1.txt

NOTA

  • Eu uso um olhar negativo para trás (?<!\.) para não tocar na primeira./
  • Eu mantenho os dirs vazios, fique à vontade para:

    find . -depth -type d -exec rm {} \;

Atenção Existem outras ferramentas com o mesmo nome que podem ou não conseguir fazer isso, portanto, tenha cuidado.

Se você executar o seguinte comando ( GNU)

$ file "$(readlink -f "$(type -p rename)")"

e você tem um resultado como

.../rename: Perl script, ASCII text executable

e não contendo:

ELF

então esta parece ser a ferramenta correta =)

Caso contrário, para torná-lo o padrão (geralmente já é o caso) Debiane derivado como Ubuntu:

$ sudo update-alternatives --set rename /path/to/rename

(substitua /path/to/renamepelo caminho do seu perl's renamecomando.


Se você não possui esse comando, procure no gerenciador de pacotes para instalá-lo ou manualmente


Por último, mas não menos importante, essa ferramenta foi originalmente escrita por Larry Wall, o pai do Perl.

Gilles Quenot
fonte
... -type d -exec rmdir ...?
JJoao
Parece que não tenho renomeação disponível. Como exatamente posso obtê-lo e isso normalmente está disponível em um sistema UNIX? Obrigado.
Nunos
@unos É um script Perl. Está disponível sob o nome perl-renameno Arch Linux e prenameem sistemas baseados no Debian.
muru
renameExplicações adicionadas
Gilles Quenot,
1

com pax...

pax -Xrwls '|/|_|g' */ "$PWD"

Isso criará um hardlink no diretório atual para todos os arquivos em seus diretórios filhos com um _substituto /. Você pode inspecionar os resultados e remover todos os diretórios filhos com ...

rm -rf */

... depois de verificar os resultados, é do seu agrado.

mikeserv
fonte
0

Você pode tentar com o comando abaixo.

$ find -type f -exec bash -c 'mv $0 $(echo $0|sed "s/\//_/2")' {} \;

O comando acima moverá os arquivos e o diretório permanecerá, podendo ser removido posteriormente.

$ ls
foo1_bar1.txt  foo1_bar2.txt  foo2_bar3.txt  foo2_bar4.txt  foo3_bar5.txt  foo3_bar6.txt
Kannan Mohan
fonte