Cria um arquivo de texto separado para listar o conteúdo em cada diretório e subdiretório

4

Eu tenho uma pasta raiz e existem muitos diretórios e arquivos. Eu preciso salvar uma lista do conteúdo em cada subdiretório com o nome list.txt .

Suponha que eu tenha

A
|
-----B
|    |---Z
|    |---a.txt
|    |---b.jpg
|
|
|----C
|    |--a.txt
|    |--b.txt

A execução do comando deve dar um list.txt em cada subdiretório com o conteúdo separado por vírgulas.

Eu # comentei qual deveria ser o conteúdo ...

A
|
-----B
|    |---Z
|    |---a.txt
|    |---b.jpg
|    |---list.txt  # Z,a.txt,b.jpg
|
|
|----C
|    |--a.txt
|    |--b.txt
|    |--list.txt  # a.txt,b.txt

O mais próximo que consegui listar os arquivos é

find . -maxdepth n -type f -printf '%f\n'

mas não sei como salvar o conteúdo separadamente.

Por favor, sugira algo.

    
por Derek James 02.04.2017 / 07:54

3 respostas

5

O script abaixo adicionará uma lista a todos os diretórios sub - de um diretório recursivamente:

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

for root, dirs, files in os.walk(sys.argv[1]):
    for dr in dirs:
        dr = os.path.join(root, dr)
        open(os.path.join(dr, "list.txt"), "wt").write(
            ",".join(f for f in os.listdir(dr) if f != "list.txt")
            )

Para usar

  1. Copie o script em um arquivo vazio, salve-o como dirlists.py
  2. Execute-o com o diretório principal como argumento:

    python3 /path/to/dirlists.py /path/to/maindirectory
    

Nota

Como mencionado, o script adiciona um list.txt a todos os subdiretórios . Se você também precisa ou quer ter uma lista na pasta main (root) -dir do seu diretório, por favor mencione.

Explicação

  1. Listar (percorrer) todos os diretórios recursivamente dentro de um diretório:

    for root, dirs, files in os.walk(sys.argv[1]):
        for dr in dirs:
            dr = os.path.join(root, dr)
    
  2. Crie uma lista de conteúdo para cada um deles:

    os.listdir(dr)
    
  3. Abra (crie, se necessário) o arquivo de texto e escreva o conteúdo listado, excluindo possíveis arquivos anteriores chamados list.txt :

    open(os.path.join(dr, "list.txt"), "wt").write(
        ",".join(f for f in os.listdir(dr) if f != "list.txt")
        )
    

EDITAR

Conforme solicitado em um comentário:

Caso você precise que a linha em list.txt termine com uma vírgula, basta substituir:

",".join(f for f in os.listdir(dr) if f != "list.txt")

por:

",".join(f for f in os.listdir(dr) if f != "list.txt")+","

note o recuo, coloque a substituição na mesma posição

    
por Jacob Vlijm 02.04.2017 / 09:19
4

Para torná-lo recursivo, primeiro ative o globstar

shopt -s globstar

Em seguida, no diretório pai ( A em sua estrutura), você pode executar:

for d in **; do [[ -d "$d" ]] && (find "$d" -mindepth 1 -maxdepth 1 \( -not -name "list.txt" \) -printf '%f,' | sed 's/,$/\n/') |tee "$d"/list.txt ; done

ou ligeiramente mais legível

for d in **; do 
  [[ -d "$d" ]] && 
  (find "$d" -mindepth 1 -maxdepth 1 \( -not -name "list.txt" \) -printf '%f,' | sed 's/,$/\n/') | tee "$d"/list.txt
done

que, se o diretório a contiver

├── a
│   ├── 1.txt
│   ├── 2.txt
│   ├── 3.txt
│   ├── a badly named file &
│   └── Z

criará uma lista no diretório a que se parece com isto:

2.txt,1.txt,3.txt,Z,a badly named file &

find não produz saída ordenada, portanto, se isso for um problema, terei que pensar em uma maneira melhor. O \( -not -name "list.txt" \) na expressão find é para evitar que a lista seja incluída, e a expressão sed é puramente para remover a vírgula final. Que vergonha sobre todos esses bytes extras.

Você pode querer desativar globstar quando terminar

shopt -u globstar
    
por Zanna 02.04.2017 / 08:43
3

Versão de uma linha

Use find para obter diretórios primeiro e, em seguida, shell faça o trabalho para você:

$ tree                                                                                                                                                
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   └── mv-files.py
└── another_dir
    ├── {file}1
    └── {file}2

3 directories, 4 files

$  find -type d -exec bash -c 'cd $1; find  -maxdepth 1  -not -name "." -not -name "list.txt" -printf "%f," | awk "{print substr(\
#!/bin/bash
# note : this assumes you run the script from top-most directory
find  -type d  | while IFS= read -r directory;
do
    cd "$directory"
    find  -maxdepth 1  -not -name "." -not -name "list.txt" -printf "%f," |
    awk "{print substr(\
$ tree                                                                                                                                                
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   └── mv-files.py
├── another_dir
│   ├── {file}1
│   └── {file}2
└── make_lists.sh

3 directories, 5 files

$ ./make_lists.sh                                                                                                                                     

$ tree
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   │   └── list.txt
│   ├── list.txt
│   └── mv-files.py
├── another_dir
│   ├── {file}1
│   ├── {file}2
│   └── list.txt
├── list.txt
└── make_lists.sh

3 directories, 9 files

$ cat a_directory/list.txt                                                                                                                            
mv-files.py,a_file,a_subdir
,0,length(\
$ tree                                                                                                                                                
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   └── mv-files.py
└── another_dir
    ├── {file}1
    └── {file}2

3 directories, 4 files

$  find -type d -exec bash -c 'cd $1; find  -maxdepth 1  -not -name "." -not -name "list.txt" -printf "%f," | awk "{print substr(\
#!/bin/bash
# note : this assumes you run the script from top-most directory
find  -type d  | while IFS= read -r directory;
do
    cd "$directory"
    find  -maxdepth 1  -not -name "." -not -name "list.txt" -printf "%f," |
    awk "{print substr(\
$ tree                                                                                                                                                
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   └── mv-files.py
├── another_dir
│   ├── {file}1
│   └── {file}2
└── make_lists.sh

3 directories, 5 files

$ ./make_lists.sh                                                                                                                                     

$ tree
.
├── a_directory
│   ├── a_file
│   ├── a_subdir
│   │   └── list.txt
│   ├── list.txt
│   └── mv-files.py
├── another_dir
│   ├── {file}1
│   ├── {file}2
│   └── list.txt
├── list.txt
└── make_lists.sh

3 directories, 9 files

$ cat a_directory/list.txt                                                                                                                            
mv-files.py,a_file,a_subdir
,0,length(\%pre%)-1)}" > list.txt cd - > /dev/null done
,0,length(\%pre%)-1)}" > list.txt' bash "{}" \; $ cat a_directory/list.txt mv-files.py,a_file,list.txt,a_subdir
)-1)}" > list.txt cd - > /dev/null done
,0,length(\%pre%)-1)}" > list.txt' bash "{}" \; $ cat a_directory/list.txt mv-files.py,a_file,list.txt,a_subdir

A maneira como isso funciona:

  • usamos o comando find com -type d para filtrar todos os diretórios
  • A instrução -exec com \; terminator nos permite executar o comando especificado para cada argumento que find obtém
  • em -exec , executamos bash com -c flag, para o qual passamos $0 argumento bash e $1 argumento sendo o diretório que outter find locaed
  • bash entrará no diretório fornecido e usará o argumento find with - maxdepth 1 para limitar esse comando apenas a esse subdiretório. -not -name "." excluirá o link do diretório . , que é referência para si mesmo.
  • Em seguida, passamos o texto para awk , que serve apenas para remover o último , dado por find , para que tenhamos uma lista de CSV válida. Observe o uso de aspas duplas e \$ . Isso significa simplificar as citações e impedir que o bash interprete $0 como seus próprios argumentos posicionais, mas sim como o comando awk .
  • Toda a lista de itens que o% internofind obtém será enviada para o list.txt via redirecionamento > .

Melhoria adicional para isso poderia ser usar -not -name "list.txt" dentro do comando find interno para excluir o próprio arquivo de lista (porque > sempre cria o arquivo para gravar primeiro, list.txt também aparecerá na lista ).

Pessoalmente, se eu estivesse fazendo isso por mim, escreveria a lista de arquivos com list.txt separator para evitar lidar com nomes de arquivo difíceis, mas isso também exige lembrar que ~/bin está em $PATH format e writing uma função parser para isso.

Versão completa do script

Por questão de legibilidade, aqui está uma versão completa do script, em vez de uma linha.

Script:

%pre%

Observe que esse script é executado no diretório mais superior. Também inclui auto na lista se estiver armazenado no mesmo diretório. Se você colocá-lo em %code% , por exemplo (ou qualquer outro diretório que pertença a %code% variable), e executá-lo, o nome do script não aparecerá na lista.

Teste:

%pre%     
por Sergiy Kolodyazhnyy 02.04.2017 / 08:55