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

11

Como eu listaria todos os diretórios que não possuem um arquivo com um nome de arquivo específico? por exemplo, dada esta árvore

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

Quero listar os diretórios que não têm um arquivo nomeado README; nesse caso, seria diretório /c. Como eu faria isso? Não consigo pensar em nenhuma sintaxe usando, por exemplo find.

Renan
fonte
Vergonha oh pena que nem sequer procurar: askubuntu.com/questions/196960/...
SLM
Não pensei nas palavras-chave certas ao pesquisar, provavelmente.
Renan
2
Eu só estou arrebentando suas costeletas. Já estive lá muitas vezes em que não conseguia pensar na palavra certa para procurar algo 8-).
slm
relacionado: askubuntu.com/questions/196960/…
Ciro Santilli escreveu:

Respostas:

5

Supondo que uma findimplementação como o GNU findque aceite 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 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

slm
fonte
Como modificar o comando para procurar subdiretórios que não possuem uma extensão de arquivo específica (digamos * .txt). Modificando README com * .txt não parece ao trabalho
WanderingMind
@WanderingMind - se você tem uma nova pergunta por favor, pergunte-lo como um novo no site ;-)
SLM
3

Você pode usar a -execopção de findpara verificar o arquivo e 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
Patrick
fonte
3

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

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

Se você precisar que seja recursivo, poderá usar (para bash, zshpode fazer isso por padrão, use set -o globstarem 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).

terdon
fonte
2

Com zshe qualificadores glob ( estring ):

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

ou

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

adicione Dpara incluir diretórios ocultos:

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

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


O exemplo acima retornará apenas nomes de diretório no diretório atual.
Se você deseja pesquisar recursivamente (novamente, para incluir diretórios ocultos, adicione o Dqualificador):

print -rl ./**/*(/e_'[[ ! -f $REPLY/README ]]'_)
don_crissti
fonte
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 está confirmado como um arquivo regular (após a resolução do link simbólico).

Se você quiser testar se ele existe apenas (como uma entrada nesse diretório), independentemente do seu tipo, você precisará:, [ -e file ] || [ -L file ]embora observe que precisa de permissão de pesquisa no diretório para executar esses testes. Você pode 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 '{}' +

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

Stéphane Chazelas
fonte