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

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

Como eu listaria todos os diretórios que fazemnãotem um arquivo com um determinado nome de arquivo dentro? por exemplo, dada esta árvore

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

Quero listar os diretórios que fazemnãotem um arquivo chamado README, neste caso seria directory /c. Como eu faria isso? Não consigo pensar em nenhuma sintaxe usando, por exemplo find.

Responder1

Assumindo uma findimplementação como GNU findque aceita um {}argumento incorporado para -exec:

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

ou, sem a incorporação problemática:

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

Exemplo

Aqui os diretórios 1/1 a 5/5 possuem 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 executamos esta versão do nosso findcomando:

$ 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

Responder2

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

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

Se precisar que seja recursivo, você pode usar (for bash, zshpode fazer isso por padrão, use set -o globstarin ksh93):

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

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

Responder3

Você pode usar a -execopção de findverificar o arquivo e, em seguida, imprimir todos os resultados cuja verificação falhou.

find /path/to/base -mindepth 1 -maxdepth 1 -type d -exec test -e {}/README \; -o -print

Responder4

Portavelmente, 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 arquivoexiste eé confirmado como um arquivo normal (após resolução do link simbólico).

Se você quisesse testar se ele existe apenas (como uma entrada nesse diretório), independentemente do seu tipo, você precisaria: [ -e file ] || [ -L file ], mas observe que você precisa de permissão de pesquisa no diretório para realizar esses testes. Você pode querer adicionar alguns [ -x "$dir" ]testes para dar conta de 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émComo posso saber se um arquivo normal não existe no Bash?em SO.

informação relacionada