Encontre diretórios nos quais faltam arquivos com um final específico


10

Quero exibir todos os diretórios que não contêm arquivos com um final de arquivo específico. Portanto, tentei usar o seguinte código:

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

Neste exemplo, eu queria exibir todos os diretórios que não contêm arquivos com o final .ENDING, mas isso não funciona.

Onde está meu erro?


Esta não é uma duplicata dos diretórios da lista que não contêm um arquivo porque essa pergunta não trata de curingas.
Cristian Ciupitu

Respostas:


7

Aqui está uma solução em três etapas:

temeraire:tmp jenny$ find . -type f -name \*ENDING -exec dirname {} \; |sort -u > /tmp/ending.lst
temeraire:tmp jenny$ find . -type d |sort -u > /tmp/dirs.lst
temeraire:tmp jenny$ comm -3 /tmp/dirs.lst /tmp/ending.lst 

2
comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Linha

4

Aqui vamos nós!

#!/usr/bin/env python3
import os

for curdir,dirnames,filenames in os.walk('.'):
  if len(tuple(filter(lambda x: x.endswith('.ENDING'), filenames))) == 0:
    print(curdir)

Ou alternadamente (e mais pitonico):

#!/usr/bin/env python3
import os

for curdir,dirnames,filenames in os.walk('.'):
    # Props to Cristian Cliupitu for the better python
    if not any(x.endswith('.ENDING') for x in filenames):
        print(curdir)

Conteúdo bônus DLC!

A versão (principalmente) corrigida do comando find:

find . -type d \! -exec bash -c 'set nullglob; test -f "{}"/*.ENDING' \; -print

Eu acho que as soluções de busca falham com test: ...: binary operator expectedse houver vários *.ENDINGarquivos em um diretório.
Cristian Ciupitu

next(filter(lambda x: x.endswith('.ENDING'), filenames))também poderia ser escrito usando a compreensão do gerador, ou seja next(x for x in filenames if x.endswith('.ENDING')).
Cristian Ciupitu

@CristianCiupitu: Verdade: o comando find é hacky e não é realmente totalmente testado. Compreensão do gerador - essa é outra ótima maneira de fazer isso.
MikeyB

1
Eu acho que uma solução ainda melhor seria usar com if not any(x.endswith('.ENDING') for x in filenames)base no fato de que qualquer retorno Falsepara um iterável vazio.
Cristian Ciupitu

3

O shell expande o *, mas no seu caso não há nenhum shell envolvido, apenas o comando test executado pelo find . Portanto, o arquivo cuja existência é testada é literalmente nomeado *.ENDING.

Em vez disso, você deve usar algo como isto:

find . -type d \! -execdir sh -c 'test -e {}/*.ENDING' \; -print

Isso resultaria em sh expansão *.ENDINGquando teste é executado.

Fonte: encontre globbing no UX.SE


Não funciona. Eu recebo o seguinte erro: sh: -c: line 0: syntax error near unexpected token (''. Os nomes dos meus diretórios têm o formato 'xyz (dfdf)'. Na verdade, é uma biblioteca de calibre.
bamboo

1
Quando tento, obtenho sh: line 0: test: foo1/bar.ENDING: binary operator expectedo diretório que contém um arquivo com o final ENDING.
Jenny D.

Isso parece funcionar para mim com nomes simples de arquivos
a.ENDING

Eu tenho a seguinte estrutura de diretório de teste: test-> test (test) -> test.ending Depois de usar esse código, recebo sh: -c: line 0: syntax error near unexpected token ('sh: -c: linha 0:' test -e test (test) / *. Terminando '). / test (teste) `Mas quando eu mudar .ending para .xyz eu obter o mesmo resultado Isto é porque eu tenho brácteas como nome do diretório, certo Como posso corrigir isso..?
bambu

3
@ambamboo Eu desistiria dos utilitários de shell e procuraria uma solução diferente neste momento.
user9517

2

Inspirado nas respostas de Dennis Nolte e MikeyB , vim com esta solução:

find . -type d                                                           \
  \! -exec bash -c 'shopt -s failglob; echo "{}"/*.ENDING >/dev/null' \; \
  -print 2>/dev/null

Funciona com base no fato de que

se a opção de shell failglob estiver configurada e nenhuma correspondência for encontrada, uma mensagem de erro será impressa e o comando não será executado.

A propósito, é por isso que o stderr foi redirecionado /dev/null.


1

Eu faria isso em perl pessoalmente

#!/usr/bin/perl

use strict;
use warnings;

use File::Find;


#this sub is called by 'find' on each file it traverses. 
sub checkdir {
    #skip non directories. 
    return unless ( -d $File::Find::name );

    #glob will return an empty array if no files math - which evaluates as 'false'
    if ( not glob ( "$File::Find::name/*.ENDING" ) ) {
        print "$File::Find::name does not contain any files that end with '.ENDING'\n";
    }
}

#kick off the search on '.' - set a directory path or list of directories to taste.
#  - you can specify multiple in a list if you so desire. 
find (  \&checkdir, "." );

Deve fazer o truque (funciona no meu caso de teste muito simplista).


0

Heres um achado one-liner.

find ./ -type d ! -regex '.*.ENDING$' -printf "%h\n" | sort -u

Edit : Opa, também não funcionará.


Não funciona porque você está testando os nomes dos diretórios, não dos arquivos dentro deles.
Cristian Ciupitu

Eu tentei outra vez. Badly
Matthew Ife

0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q no egrep é para silêncio

  • Com egrepvocê pode trocar o regex que você precisa

  • ls -1 "{}" gera os nomes de arquivo do comando find

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.