Encuentra directorios, donde faltan archivos con un final específico


10

Quiero mostrar todos los directorios que no contienen archivos con un final de archivo específico. Por lo tanto, intenté usar el siguiente código:

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

En este ejemplo, quería mostrar todos los directorios que no contienen archivos con el final .ENDING, pero esto no funciona.

¿Dónde está mi error?


Este no es un duplicado de los directorios de Lista que no contienen un archivo porque esa pregunta no trata con comodines.
Cristian Ciupitu

Respuestas:


7

Aquí hay una solución en tres pasos:

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 sin archivos temporales si usa el shell Bash:comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Cristian Ciupitu

4

¡Aquí vamos!

#!/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 (y más pitónico):

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

¡Contenido extra de contenido descargable!

La versión (en su mayoría) corregida del comando find:

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

Creo que las soluciones de búsqueda fallan test: ...: binary operator expectedsi hay varios *.ENDINGarchivos en un directorio.
Cristian Ciupitu

next(filter(lambda x: x.endswith('.ENDING'), filenames))también podría escribirse usando la comprensión del generador, es decir next(x for x in filenames if x.endswith('.ENDING')).
Cristian Ciupitu

@CristianCiupitu: Verdad: el comando find es hacky y no está completamente probado. Comprensión del generador: sí, esa es otra buena manera de hacerlo.
MikeyB

1
Creo que una solución aún más agradable sería usar en if not any(x.endswith('.ENDING') for x in filenames)base al hecho de que cualquier retorno Falsepara un iterable vacío.
Cristian Ciupitu

3

El shell expande el *, pero en su caso no hay shell involucrado, solo el comando de prueba ejecutado por find . Por lo tanto, el archivo cuya existencia se prueba se llama literalmente *.ENDING.

En su lugar, debería usar algo como esto:

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

Esto daría como resultado que sh se expanda *.ENDINGcuando se ejecute la prueba .

Fuente: encuentra globbing en UX.SE


No funciona. Me sale el siguiente error: sh: -c: line 0: syntax error near unexpected token (''. Mis nombres de directorio tienen el formato 'xyz (dfdf)'. De hecho, es una biblioteca de calibre.
bambú

1
Cuando lo intento, obtengo sh: line 0: test: foo1/bar.ENDING: binary operator expectedel directorio que contiene un archivo con el final ENDING.
Jenny D

Esto parece funcionar para mí con nombres de archivo simples a.ENDING
user9517

Tengo la siguiente estructura de directorio de prueba: prueba-> prueba (prueba) -> prueba.ending Después de usar este código obtengo sh: -c: line 0: syntax error near unexpected token ('sh: -c: línea 0:' prueba -e prueba (prueba) / *. Terminando '. / test (test) ". Pero cuando cambio .ending a .xyz obtengo el mismo resultado. Esto se debe a que tengo brácteas como nombre de directorio, ¿verdad? ¿Cómo puedo solucionarlo?
bambú

3
@bamboo Me rendiría con las utilidades de shell y buscaría una solución diferente en este momento.
user9517

2

Inspirado por las respuestas de Dennis Nolte y MikeyB , se me ocurrió esta solución:

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

Funciona basado en el hecho de que

Si se establece la opción de shell failglob y no se encuentran coincidencias, se imprime un mensaje de error y el comando no se ejecuta.

Por cierto, por eso se redirigió a stderr/dev/null .


1

Lo haría personalmente en Perl

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

Debería hacer el truco (funciona en mi caso de prueba muy simplista).


0

Aquí hay un hallazgo de una sola línea.

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

Editar : Vaya, tampoco funcionará.


No funciona porque está probando los nombres de los directorios, no de los archivos dentro de ellos.
Cristian Ciupitu

Lo he intentado de nuevo. Malo
Matthew Ife

0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q en egrep es para tranquilidad

  • Con egrepusted puede intercambiar la expresión regular que necesita

  • ls -1 "{}" genera los nombres de archivo del comando find

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.