Como posso recursivamente criar um diretório nos últimos diretórios usando a linha de comando?

5

Digamos que eu tenha a seguinte estrutura de diretórios onde o nome dos diretórios não importa:

currentDir/
   Dir1
      Dir1
         Dir1 *
      Dir2 *
      Dir3 *
   Dir2
      Dir1 *
      Dir2 
         Dir1*
   Dir3*

Existe uma maneira de criar um diretório chamado "Textura" somente nos diretórios no final da ramificação da árvore (no exemplo, eles estão marcados com * ). Eu gostaria de fazer com que apenas esses diretórios recebessem um diretório Texture , mas nenhum outro.

Todos os diretórios podem ou não ter arquivos, mas os diretórios finais não terão outros diretórios neles.

    
por kingcobra1986 26.09.2017 / 21:14

4 respostas

6

Primeiro, ligue a globalização recursiva

shopt -s globstar

Agora podemos procurar em todos os diretórios com ** . Para cancelar a configuração, você pode usar shopt -u globstar (está desativado por padrão, portanto, será desativado quando você abrir um novo shell).

Em todos os itens abaixo, remova echo após o teste para criar os diretórios.

Se os diretórios de fim de árvore estiverem vazios, você pode testar o vazio e criar o diretório apenas se for detectado ... algo como

for d in **/; do [[ -z "$(ls -Aq "$d")" ]] && echo mkdir "$d"Texture; done

i.e. se a saída de ls -A (hidden files except current and parent dir) -q (printing ? for non-printing characters) estiver vazia, crie um diretório.

Mas como os diretórios contêm arquivos, é melhor testarmos que não há diretórios

for d in **/; do [[ -z "$(find "$d" -maxdepth 1 -mindepth 1 -type d)" ]] && echo mkdir "$d"Texture; done

mindepth -1 é para impedir que o diretório atual seja encontrado ... find vê arquivos ocultos, por isso sabemos que eles estão incluídos. Como qualquer personagem deve passar no teste (queremos apenas verificar se a saída está vazia), isso deve ser bastante seguro ...

Uma maneira mais concisa (desculpe Greg) (mas, na verdade, todos esses métodos fazem algo similarmente duvidoso - podemos simplesmente fugir com o uso de nomes de arquivos como texto, já que na verdade não estamos tentando fazer nada com esses arquivos aqui):

for d in **/; do (ls -Alq "$d" | grep -q '^d') || echo mkdir "$d"Textures; done

aqui adicionamos o sinalizador -l para a saída longa, que imprime todos os arquivos no formato

drwxrwxr-x 5 zanna zanna 4096 Sep 27 22:00 Dir1

nós então gulp canalizamos esse material em grep para ver se alguma das linhas começa com d (o que significa que há um diretório). Não queremos que grep imprima nada desse texto não-confiável sem sentido, então apenas verificamos seu status de saída com -q para ver se encontrou algo. Caso contrário, criamos um diretório (usando o || ou operador, ou seja, faça o próximo comando se o último falhar). Isso deve funcionar, na verdade, mesmo que seus nomes de arquivo contenham caracteres horríveis como newlines, pois ls os imprime como ? - cada linha deve iniciar com uma letra indicando o tipo de arquivo, então provavelmente nenhum gatinho morrerá.

    
por Zanna 26.09.2017 / 21:46
5

Aqui está uma dolorosa chamada find -exec

Antes:

$ tree currentDir
currentDir
├── Dir1
│   ├── Dir1
│   │   └── Dir1
│   ├── Dir2
│   └── Dir3
├── Dir2
│   ├── Dir1
│   └── Dir2
│       └── Dir1
└── Dir3

Crie os diretórios Texture

$ find currentDir -depth -type d -exec bash -c 'cd "$1"; shopt -s globstar nullglob; texture=(**/Texture); [[ ${#texture[@]} -eq 0 ]] && mkdir Texture' _ '{}' \;

Isso depende de:

  • A opção -depth do find para ativar a navegação em profundidade
  • opção globstar shell do bash para ativar a correspondência de padrões de arquivos recursivos
  • observe o argumento _ : que vai para o bash var $0 para que o nome do caminho atual do find possa ser bash var $1

Depois

$ tree currentDir
currentDir
├── Dir1
│   ├── Dir1
│   │   └── Dir1
│   │       └── Texture
│   ├── Dir2
│   │   └── Texture
│   └── Dir3
│       └── Texture
├── Dir2
│   ├── Dir1
│   │   └── Texture
│   └── Dir2
│       └── Dir1
│           └── Texture
└── Dir3
    └── Texture
    
por glenn jackman 26.09.2017 / 21:40
3

Isso deve funcionar:

shopt -s globstar; for i in **/; do sub=$(find "$i" -type d | wc -l); if [[ "$sub" -eq 1 ]]; then mkdir "$i"/Texture; fi;done

Resultado:

currentDir
├── Dir1
│   ├── Dir1
│   │   └── Dir1
│   │       └── Texture
│   ├── Dir2
│   │   └── Texture
│   └── Dir3
│       └── Texture
├── Dir2
│   ├── Dir1
│   │   └── Texture
│   └── Dir2
│       └── Dir1
│           └── Texture
└── Dir3
    └── Texture
    
por George Udosen 26.09.2017 / 21:21
1

Python torna isso fácil.

python3 -c 'import os
for d in [root for root, dirs, files in os.walk(".") if not any(dirs)]:
    os.mkdir(d + "/Texture")'

Você pode executar esse comando a partir do seu shell. Ele passa um script de três linhas para o interpretador do Python 3 , que o executa. O script cria uma lista de todos os diretórios em . (aquele em que você está) e, em seguida, cria um subdiretório Texture em cada um que ainda não possui subdiretórios.

Isso é mais simples de escrever e mais fácil de entender do que a maioria dos outros métodos, porque o Python tem uma os.walk função que enumera diretórios de uma forma que facilita ver quais têm subdiretórios (e mais geralmente porque Python é uma linguagem que muitos usuários acham limpa e intuitiva).

Se você quiser apenas mostrar quais novos diretórios seriam criados em vez de criá-los, substitua os.mkdir com print . Aqui está, no caso de você querer copiar e colar:

python3 -c 'import os
for d in [root for root, dirs, files in os.walk(".") if not any(dirs)]:
    print(d + "/Texture")'

Existem maneiras de escrever um desses comandos de três linhas em uma única linha, mas não há necessidade. Você pode digitar o comando que deseja usar em um prompt interativo digitando ou colando-o, ou pode usá-lo em um shell script. Quando inserido interativamente, seu shell imprimirá > (a menos que você tenha definido $PS2 para algo diferente) no início da segunda e terceira linhas. Isso não é um problema.

O precedente assume que você está usando um shell estilo Bourne como bash , dash / sh , ksh , zsh , posh , etc. ( Aqueles três respostas use shopt -s globstar Esse comando é específico do Bash, apesar de alguns outros shells terem um recurso parecido. Então, se eles trabalharem para você, esse sho uld também.) Se você está usando o Ubuntu e não sabe em que shell está digitando comandos, é bash . Se você estiver usando csh ou tcsh , você terá que colocar um \ nas extremidades das duas primeiras linhas para evitar um erro Unmatched '. e você verá ? em vez de > .

Para testar esse método em uma hierarquia de diretório existente sem criar os diretórios, basta usar o segundo comando conforme descrito acima. Mas, se quiser, você pode criar uma nova hierarquia de diretórios em algum lugar apenas para fins de teste. mkdir -p dir/{a/{b/c,d,e},f/{g,h/i},j} cria uma árvore de diretórios com a mesma topologia que a do seu exemplo; find dir -type d -exec touch {}/x \; preenche-o com alguns arquivos para que você possa verificar se a presença de não-diretórios não é um problema; cd dir entra nele; e então você pode testar os comandos mostrados acima, assim como qualquer outro método que você possa estar interessado, como aqueles apresentados nas outras respostas; em seguida, execute tree .

    
por Eliah Kagan 27.09.2017 / 11:41