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.