Rechercher des répertoires où des fichiers avec une fin spécifique sont manquants

10

Je veux afficher tous les répertoires qui ne contiennent pas de fichiers avec une fin de fichier spécifique. J'ai donc essayé d'utiliser le code suivant:

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

Dans cet exemple, je voulais afficher tous les répertoires, qui ne contiennent pas de fichiers avec la fin .ENDING, mais cela ne fonctionne pas.

Où est mon erreur?

bambou
la source
Il ne s'agit pas d'un doublon de répertoires List ne contenant pas de fichier car cette question ne concerne pas les caractères génériques.
Cristian Ciupitu

Réponses:

7

Voici une solution en trois étapes:

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 
Jenny D
la source
2
One-liner sans fichiers temporaires si vous utilisez le shell Bash:comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Cristian Ciupitu
4

Et c'est parti!

#!/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 alternativement (et plus pythonique):

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

Contenu bonus DLC!

La version (principalement) corrigée de la commande find:

find . -type d \! -exec bash -c 'set nullglob; test -f "{}"/*.ENDING' \; -print
MikeyB
la source
Je pense que les solutions de recherche échouent test: ...: binary operator expecteds'il y a plusieurs *.ENDINGfichiers dans un répertoire.
Cristian Ciupitu
next(filter(lambda x: x.endswith('.ENDING'), filenames))pourrait également être écrit en utilisant la compréhension du générateur ie next(x for x in filenames if x.endswith('.ENDING')).
Cristian Ciupitu
@CristianCiupitu: Vérité: la commande find est hacky et pas vraiment entièrement testée. Compréhension du générateur - oui, c'est une autre belle façon de le faire.
MikeyB
1
Je pense qu'une solution encore plus agréable serait d'utiliser en if not any(x.endswith('.ENDING') for x in filenames)fonction du fait que tout retourne Falsepour un itérable vide.
Cristian Ciupitu
3

Le shell étend le *, mais dans votre cas, aucun shell n'est impliqué, juste la commande de test exécutée par find . D'où le fichier dont l'existence est testée, est littéralement nommé *.ENDING.

Au lieu de cela, vous devez utiliser quelque chose comme ceci:

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

Cela entraînerait une expansion de sh*.ENDING lors de l' exécution du test .

Source: trouver globbing sur UX.SE

Dennis Nolte
la source
Ne marche pas. J'obtiens l'erreur suivante: sh: -c: line 0: syntax error near unexpected token (''. Mes noms de répertoire ont le format 'xyz (dfdf)'. En fait, c'est une bibliothèque de calibre.
bamboo
1
Quand je l'essaye, j'obtiens sh: line 0: test: foo1/bar.ENDING: binary operator expectedle répertoire contenant un fichier avec la fin ENDING.
Jenny D
Cela semble fonctionner pour moi avec les noms de fichiers plain a.ENDING
user9517
J'ai la structure de répertoire de test suivante: test-> test (test) -> test.ending Après avoir utilisé ce code, j'obtiens sh: -c: line 0: syntax error near unexpected token ('sh: -c: ligne 0:' test -e test (test) / *. Fin '). / test (test) `. Mais quand je change .ending en .xyz j'obtiens le même résultat. C'est parce que j'ai des bractées comme nom de répertoire, non? Comment puis-je résoudre ce problème?
bamboo
3
@bamboo J'abandonnerais les utilitaires shell et chercherais une solution différente à ce stade.
user9517
2

Inspiré par les réponses de Dennis Nolte et MikeyB , j'ai trouvé cette solution:

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

Cela fonctionne basé sur le fait que

si l' option shell failglob est définie et qu'aucune correspondance n'est trouvée, un message d'erreur est imprimé et la commande n'est pas exécutée.

Au fait, c'est pourquoi stderr a été redirigé vers /dev/null.

Cristian Ciupitu
la source
1

Je le ferais en perl personnellement

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

Devrait faire l'affaire (fonctionne sur mon cas de test très simpliste).

Sobrique
la source
0

Voici une trouvaille.

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

Edit : Oups, ne fonctionnera pas non plus.

Matthew Ife
la source
Cela ne fonctionne pas car vous testez les noms des répertoires, pas des fichiers qu'ils contiennent.
Cristian Ciupitu
Je l'ai réessayé. Mal
Matthew Ife
0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q en egrep est pour le calme

  • Avec, egrepvous pouvez échanger l'expression régulière dont vous avez besoin

  • ls -1 "{}" affiche les noms de fichiers à partir de la commande find

sk8asd123
la source