Lista todos os diretórios que NÃO possuem um arquivo com um determinado nome de arquivo dentro

9

Como eu entraria na listagem de todos os diretórios que não possuem um arquivo com um determinado nome de arquivo? por exemplo. dada esta árvore

/
  /a
     README
     file001
     file002
  /b
     README
     file001
  /c
     file003

Eu quero listar os diretórios que não possuem um arquivo chamado README , nesse caso seria o diretório /c . Como eu faria isso? Não consigo pensar em nenhuma sintaxe usando, por exemplo, find .

    
por Renan 04.08.2013 / 02:22

5 respostas

5

Assumindo uma implementação de find como o GNU find que aceita um {} incorporado em um argumento para -exec :

$ find . -type d \! -exec test -e '{}/README' \; -print

Exemplo

Aqui os diretórios 1/1 a 5/5 têm um README, os outros diretórios estão vazios.

$ tree 
.
|-- 1
|   '-- 1
|       '-- README
|-- 10
|   '-- 10
|-- 2
|   '-- 2
|       '-- README
|-- 3
|   '-- 3
|       '-- README
|-- 4
|   '-- 4
|       '-- README
|-- 5
|   '-- 5
|       '-- README
|-- 6
|   '-- 6
|-- 7
|   '-- 7
|-- 8
|   '-- 8
'-- 9
    '-- 9

Agora, quando executarmos esta versão do nosso comando find :

$ find . -type d \! -exec test -e '{}/README' \; -print
.
./10
./10/10
./7
./7/7
./9
./9/9
./6
./6/6
./5
./8
./8/8
./4
./1
./3
./2

Referências

por 04.08.2013 / 03:18
3

Você pode usar a opção -exec de find para verificar o arquivo e, em seguida, imprimir todos os resultados para os quais a verificação falha.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print
    
por 04.08.2013 / 02:37
3

Não há necessidade de find . Basta usar o shell:

for d in */; do [ -f "$d"README ] || printf '%s\n' "$d"; done
c/

Se você precisar que ele seja recursivo, você pode usar (por bash , zsh pode fazer isso por padrão, use set -o globstar em ksh93 ):

shopt -s globstar
for d in **/; do [ -f "$d"README ] || printf '%s\n' "$d"; done

(note que os arquivos de ponto são excluídos por padrão).

    
por 11.09.2015 / 18:42
2

Com zsh e qualificadores de glob ( e string ):

print -rl -- *(/e_'[[ ! -f $REPLY/README ]]'_)

ou

print -rl -- *(/^e_'[[ -f $REPLY/README ]]'_)

adicione D para incluir diretórios ocultos:

print -rl -- *(D/e_'[[ ! -f $REPLY/README ]]'_)

/ seleciona apenas diretórios e e_'[[ ! -f $REPLY/README ]]'_ seleciona apenas os nomes de diretório para os quais o código de shell entre as aspas retorna true , ou seja, para cada nome de diretório ( $REPLY ) que a glob *(/) expande para, ele executa [[ ! -f $REPLY/README ]] e mantém o nome do diretório se o resultado for true .
A segunda forma ^e_'.....'_ usa o mesmo qualificador glob, negado (mas desta vez a expressão condicional não é negada: [[ -f $REPLY/README ]] ).

Os itens acima só retornarão nomes de diretórios no diretório atual.
Se você quiser pesquisar recursivamente (novamente, incluir diretórios ocultos, adicione o D qualificador):

print -rl ./**/*(/e_'[[ ! -f $REPLY/README ]]'_)
    
por 23.08.2015 / 13:19
2

Portably, você poderia fazer:

find . -type d -exec sh -c '
  for dir do
    [ -f "$dir/README" ] || printf "%s\n" "$dir"
  done' sh '{}' +

[ -f file ] testa se o arquivo existe e é confirmado como sendo um arquivo regular (após a resolução do symlink).

Se você quisesse testar se existe apenas (como uma entrada nesse diretório), independentemente do tipo, você precisaria de: [ -e file ] || [ -L file ] , mas observe que é necessário ter permissão de pesquisa no diretório para realizar esses testes. Você pode adicionar alguns testes [ -x "$dir" ] para considerar os casos como:

find . -type d -exec sh -c '
  for dir do
    if [ -x "$dir" ]; then
      [ -f "$dir/README" ] || printf "%s\n" "$dir"
    else
      printf >&2 "Cannot tell for \"%s\"\n" "$dir"
    fi
  done' sh '{}' +

Ou para evitar a condição de corrida, com zsh :

find . -type d -exec zsh -c '
  zmodload zsh/system
  for dir do
    ERRNO=0
    if [ ! -f "$dir/README" ]; then
      if [ "$errnos[ERRNO]" = ENOENT ]; then
        printf "%s\n" "$dir"
      else
        syserror -p "ERROR: $dir/README: "
      fi
    fi
  done' zsh '{}' +

Veja também Como posso saber se um arquivo regular não existe no Bash? no SO.

    
por 20.09.2017 / 15:32