Como recursivamente obtenho todos os diretórios que contêm um arquivo txt com o mesmo nome do diretório contido no Bash?

2

Digamos que eu tenha o seguinte:

./
   Dir1/
      Sub1/
           Sub1.jpg
      Sub2/
           Sub2.jpg
           Sub2.png
           Sub2.txt
   Dir2/
      Sub1/
           Sub1.doc
           Sub1.txt
      Sub2/
           Sub2.jpg
           Sub2.png
           SomeTxt.txt
   Dir3/
      Dir3.txt
   Dir4/
      Sub1/
           Dir4.txt
      Some.txt

Usando o bash (bashrc no meu caso), como posso obter uma variável que contenha todos os diretórios contendo um arquivo de texto (.txt) com o mesmo nome do diretório que contém? Então, a partir do exemplo acima, eu gostaria de ter uma variável contendo o seguinte:

./Dir1/Sub2/    
./Dir1/Sub1/
./Dir3/

Ele não deve retornar ./Dir1/Sub1/ porque não tem um arquivo Sub1.txt .

Ele não deve retornar ./Dir2/Sub2/ porque o arquivo txt não é Sub2.txt .

Ele não deve devolver ./Dir4/ ou ./Dir4/Sub1/ porque Dir4.txt não está diretamente em ./Dir4/ .

NOTA :

Os nomes dos diretórios não precisam ser esses nomes em particular. O importante é que o nome do arquivo txt corresponda ao nome do diretório contido.

Além disso, sei que outras línguas podem ser mais fáceis, mas preciso delas no bash.

    
por kingcobra1986 28.09.2017 / 00:18

2 respostas

3

Dado

$ tree somepath/
somepath/
├── Dir1
│   ├── Sub1
│   │   └── Sub1.jpg
│   └── Sub2
│       ├── Sub2.jpg
│       ├── Sub2.png
│       └── Sub2.txt
├── Dir2
│   ├── Sub1
│   │   ├── Sub1.doc
│   │   └── Sub1.txt
│   └── Sub2
│       ├── SomeTxt.txt
│       ├── Sub2.jpg
│       └── Sub2.png
├── Dir3
│   └── Dir3.txt
└── Dir4
    ├── Some.txt
    └── Sub1
        └── Dir4.txt

9 directories, 12 files

depois, com a opção globstar do shell ativada ( shopt -s globstar )

for d in somepath/**/; do
    d="${d%/}"
    [[ -f "${d}/${d##*/}.txt" ]] && printf "%s\n" "$d"
done
somepath/Dir1/Sub2
somepath/Dir2/Sub1
somepath/Dir3

Se você quiser armazenar os resultados em uma matriz para processamento posterior, você pode fazer isso usando o mapfile do shell (ou seu sinônimo readarray ), por exemplo,

mapfile -t TXT_DIRS < <(
  for d in somepath/**/; do
    d="${d%/}"
    [[ -f "${d}/${d##*/}.txt" ]] && printf "%s\n" "$d"
  done
)

OBSERVAÇÃO: seria melhor praticar terminar a lista nula usando um dos métodos discutidos em bash: uso processual seguro de espaço de localização em

Você pode, então, iterar pelo array assim

for d in "${TXT_DIRS[@]}"; do
    echo "$d"
done
somepath/Dir1/Sub2
somepath/Dir2/Sub1
somepath/Dir3

Como alternativa, você pode evitar o armazenamento dos resultados e simplesmente processar cada diretório à medida que o encontra, por exemplo

for d in somepath/**/; do 
    d="${d%/}"
    [[ -f "${d}/${d##*/}.txt" ]] && echo "Doing something with $d"
done
Doing something with somepath/Dir1/Sub2
Doing something with somepath/Dir2/Sub1
Doing something with somepath/Dir3
    
por steeldriver 28.09.2017 / 00:52
0

Isso é praticamente o mesmo que a resposta do steeldriver, mas faz um loop sobre cada arquivo txt em vez de sobre cada diretório. Espero que seja mais fácil de entender também.

Como você mencionou em um comentário que não precisa de um array, deixei de fora essa etapa.

shopt -s globstar
for f in ./**/*.txt; do
    d="$(dirname "$f")"
    if [[ $(basename "$f") == $(basename "$d").txt ]]; then
        printf '%s\n' "$d"
    fi
done

Saída:

./Dir1/Sub2
./Dir2/Sub1
./Dir3
    
por wjandrea 12.10.2017 / 20:46