Trova le directory in cui mancano i file con un finale specifico


10

Voglio visualizzare tutte le directory che non contengono file con un finale di file specifico. Pertanto ho provato a utilizzare il seguente codice:

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

In questo esempio ho voluto visualizzare tutte le directory, che non contengono file con il finale .ENDING, ma questo non funziona.

Dov'è il mio errore?


Questo non è un duplicato delle directory di elenco che non contengono un file perché quella domanda non ha a che fare con i caratteri jolly.
Cristian Ciupitu,

Risposte:


7

Ecco una soluzione in tre passaggi:

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
One-liner senza file temporanei se si utilizza la shell Bash:comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Cristian Ciupitu,

4

Eccoci qui!

#!/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)

O alternativamente (e più pitone):

#!/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)

Contenuto DLC bonus!

La versione (principalmente) corretta del comando find:

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

Penso che le soluzioni di ricerca falliscano test: ...: binary operator expectedse ci sono più *.ENDINGfile in una directory.
Cristian Ciupitu,

next(filter(lambda x: x.endswith('.ENDING'), filenames))potrebbe anche essere scritto usando la comprensione del generatore cioè next(x for x in filenames if x.endswith('.ENDING')).
Cristian Ciupitu,

@CristianCiupitu: Verità: il comando find è confuso e non è stato completamente testato. Comprensione del generatore - sì, è un altro bel modo per farlo.
MikeyB,

1
Penso che una soluzione ancora migliore sarebbe usare in if not any(x.endswith('.ENDING') for x in filenames)base al fatto che qualsiasi ritorno Falseper un iterable vuoto.
Cristian Ciupitu,

3

La shell espande la *, ma nel tuo caso non è coinvolta alcuna shell, solo il comando test eseguito da find . Quindi il file la cui esistenza è testata, è letteralmente chiamato *.ENDING.

Invece dovresti usare qualcosa del genere:

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

Ciò comporterebbe l' espansione di sh*.ENDING quando viene eseguito il test .

Fonte: trova globbing su UX.SE


Non funziona. Ottengo il seguente errore: sh: -c: line 0: syntax error near unexpected token (''. I miei nomi di directory hanno il formato 'xyz (dfdf)'. In effetti è una libreria di calibro.
bamboo

1
Quando lo provo, ottengo sh: line 0: test: foo1/bar.ENDING: binary operator expectedla directory contenente un file con il finale ENDING.
Jenny D,

Questo sembra funzionare per me con semplici nomi di file
.INGING

Ho la seguente struttura di directory di test: test-> test (test) -> test.ending Dopo aver usato questo codice ottengo sh: -c: line 0: syntax error near unexpected token ('sh: -c: linea 0:' test -e test (test) / *. Ending '. / test (test) ". Ma quando cambio .ending in .xyz ottengo lo stesso risultato. Questo perché ho brattee come nome di directory, giusto? Come posso sistemarlo?
bamboo

3
@bamboo Vorrei rinunciare alle utility di shell e cercare una soluzione diversa a questo punto.
user9517

2

Ispirato dalle risposte di Dennis Nolte e MikeyB , ho trovato questa soluzione:

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

Funziona in base al fatto che

se l' opzione shell failglob è impostata e non viene trovata alcuna corrispondenza, viene stampato un messaggio di errore e il comando non viene eseguito.

A proposito, ecco perché stderr è stato reindirizzato a /dev/null.


1

Lo farei in perl personalmente

#!/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, "." );

Dovrebbe fare il trucco (funziona sul mio caso di test molto semplicistico).


0

Ecco un one-liner di ricerca.

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

Modifica : Oops, non funzionerà neanche.


Non funziona perché stai testando i nomi delle directory, non dei file al loro interno.
Cristian Ciupitu,

Ci ho provato di nuovo. Badly
Matthew Ife,

0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q in egrep è per la tranquillità

  • Con egrepte puoi scambiare la regex di cui hai bisogno

  • ls -1 "{}" genera i nomi dei file dal comando find

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.