desenv-web-rp.com

Como percorrer apenas diretórios no bash?

Eu tenho uma pasta com alguns diretórios e alguns arquivos (alguns estão ocultos, começando com ponto).

for d in *; do
 echo $d
done

irá percorrer todos os arquivos, mas eu quero percorrer apenas os diretórios. Como faço isso?

287
rubo77

Se você precisar selecionar arquivos mais específicos do que apenas os diretórios, use find e passe-o para while read:

shopt -s dotglob
find * -Prune -type d | while IFS= read -r d; do 
    echo "$d"
done

Usar shopt -u dotglob para excluir diretórios ocultos (ou setopt dotglob/unsetopt dotglob em zsh).

IFS= para evitar a divisão de nomes de arquivos contendo um dos $IFS, por exemplo: 'a b'

veja resposta da AsymLabs abaixo para mais opções de find


editar:
Caso você precise criar um valor de saída a partir do loop while, é possível contornar o subshell extra com este truque:

while IFS= read -r d; do 
    if [ "$d" == "something" ]; then exit 1; fi
done < <(find * -Prune -type d)
24
rubo77

Você pode especificar uma barra no final para corresponder apenas aos diretórios:

for d in */ ; do
    echo "$d"
done
374
choroba

Você pode testar com -d:

for f in *; do
    if [ -d "$f" ]; then
        # $f is a directory
    fi
done

Este é um dos operadores de teste de arquivo .

85
goldilocks

Cuidado que a solução do choroba, embora elegante, pode provocar um comportamento inesperado se nenhum diretório estiver disponível no diretório atual. Nesse estado, em vez de pular o loop for, o bash executará o loop exatamente uma vez em que d seja igual a */:

#!/usr/bin/env bash

for d in */; do
    # Will print */ if no directories are available
    echo $d
done

Eu recomendo usar o seguinte para proteger contra este caso:

#!/usr/bin/env bash

for f in *; do
    if [ -d ${f} ]; then
        # Will not run if no directories are available
        echo $f
    fi
done

Esse código percorrerá todos os arquivos no diretório atual, verifique se f é um diretório e, em seguida, ecoa f se a condição retornar verdadeira. Se f for igual a */, echo $f não será executado.

34
emagdne

Você pode usar o bash puro para isso, mas é melhor usar o find:

find . -maxdepth 1 -type d -exec echo {} \;

(encontrar adicionalmente incluirá diretórios ocultos)

12
rush

Isso é feito para localizar os diretórios visíveis e ocultos no atual diretório de trabalho, excluindo o diretório raiz:

apenas percorrer os diretórios:

 find -path './*' -Prune -type d

para incluir links simbólicos no resultado:

find -L -path './*' -Prune -type d

para fazer algo em cada diretório (excluindo links simbólicos):

find -path './*' -Prune -type d -print0 | xargs -0 <cmds>

excluir diretórios ocultos:

find -path './[^.]*' -Prune -type d

para executar vários comandos nos valores retornados (um exemplo muito artificial):

find -path './[^.]*' -Prune -type d -print0 | xargs -0 -I '{}' sh -c \
"printf 'first: %-40s' '{}'; printf 'second: %s\n' '{}'"

em vez de 'sh -c' também pode usar 'bash -c', etc.

7
AsymLabs

Isso incluirá o caminho completo em cada diretório da lista:

for i in $(find $PWD -maxdepth 1 -type d); do echo $i; done
3
user26053

Você pode percorrer todos os diretórios, incluindo os diretórios ocultos (começando com um ponto) em uma linha e vários comandos com:

for file in */ .*/ ; do echo "$file is a directory"; done

Se você deseja excluir links simbólicos:

for file in *; do 
  if [[ -d "$file" && ! -L "$file" ]]; then
    echo "$file is a directory"; 
  fi; 
done

nota: sando a lista */ .*/ funciona no bash, mas também exibe as pastas . e .. enquanto estiver no zsh, ele não será exibido, mas gerará um erro se não houver um arquivo oculto na pasta

Uma versão mais limpa que incluirá diretórios ocultos e excluirá ../ estará com a opção dotglob:

shopt -s dotglob
for file in */ ; do echo "$file is a directory"; done

(ou setopt dotglob em zsh)

você pode desabilitar o dotglob com

shopt -u dotglob
3
rubo77

Use find com -exec para percorrer os diretórios e chamar um função na opção exec:

dosomething () {
  echo "doing something with $1"
}
export -f dosomething
find -path './*' -Prune -type d -exec bash -c 'dosomething "$0"' {} \;

Usar shopt -s dotglob ou shopt -u dotglob para incluir/excluir diretórios ocultos

0
rubo77
ls -d */ | while read d
do
        echo $d
done
0
dcat

Isso lista todos os diretórios, juntamente com o número de subdiretórios em um determinado caminho:

for directory in */ ; do D=$(readlink -f "$directory") ; echo $D = $(find "$D" -mindepth 1 -type d | wc -l) ; done
0
pabloa98