Znajdź katalogi, w których brakuje plików o określonym zakończeniu

10

Chcę wyświetlić wszystkie katalogi, które nie zawierają plików z konkretnym zakończeniem. Dlatego próbowałem użyć następującego kodu:

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

W tym przykładzie chciałem wyświetlić wszystkie katalogi, które nie zawierają plików z końcówką .ENDING, ale to nie działa.

Gdzie jest mój błąd?

bambus
źródło
To nie jest duplikat katalogów List nie zawierających pliku, ponieważ to pytanie nie dotyczy symboli wieloznacznych.
Cristian Ciupitu

Odpowiedzi:

7

Oto rozwiązanie w trzech krokach:

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.
źródło
2
One-liner bez plików tymczasowych, jeśli używasz powłoki Bash:comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Cristian Ciupitu
4

No to ruszamy!

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

Lub naprzemiennie (i bardziej pythonowo):

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

Dodatkowa zawartość DLC!

(Przeważnie) poprawiona wersja polecenia find:

find . -type d \! -exec bash -c 'set nullglob; test -f "{}"/*.ENDING' \; -print
MikeyB
źródło
Myślę, że znalezienie rozwiązań nie powiedzie się, test: ...: binary operator expectedjeśli *.ENDINGw katalogu jest wiele plików.
Cristian Ciupitu
next(filter(lambda x: x.endswith('.ENDING'), filenames))można również napisać przy użyciu zrozumienia generatora, tj next(x for x in filenames if x.endswith('.ENDING')).
Cristian Ciupitu
@CristianCiupitu: Truth: polecenie find jest zhackowane i nie do końca przetestowane. Zrozumienie generatora - tak, to kolejny fajny sposób na zrobienie tego.
MikeyB
1
Myślę, że jeszcze lepszym rozwiązaniem byłoby użycie w if not any(x.endswith('.ENDING') for x in filenames)oparciu o fakt, że wszelkie zwroty Falsedla pustego iterowalnego.
Cristian Ciupitu
3

Powłoka rozszerza *, ale w twoim przypadku nie ma w niej udziału, wystarczy polecenie testowe wykonane przez find . Dlatego plik, którego istnienie jest testowane, ma dosłownie nazwę *.ENDING.

Zamiast tego powinieneś użyć czegoś takiego:

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

Spowodowałoby to rozszerzenie sh*.ENDING podczas wykonywania testu .

Źródło: znajdź globbing na UX.SE

Dennis Nolte
źródło
Nie działa. Otrzymuję następujący błąd: sh: -c: line 0: syntax error near unexpected token (''. Moje nazwy katalogów mają format 'xyz (dfdf)'. W rzeczywistości jest to biblioteka kalibru.
bambus
1
Kiedy próbuję, szukam sh: line 0: test: foo1/bar.ENDING: binary operator expectedkatalogu zawierającego plik z końcówką ENDING.
Jenny D.
Wydaje mi się, że to działa dla mnie z prostymi nazwami plików
a.ENDING
Mam następującą strukturę katalogu testów: test-> test (test) -> test.ending Po użyciu tego kodu otrzymuję sh: -c: line 0: syntax error near unexpected token ('sh: -c: linia 0:' test -e test (test) / *. Ending ''). / test (test) `. Ale kiedy zmieniam .ending do .xyz, otrzymuję ten sam wynik. To dlatego, że mam przylistki jako nazwę katalogu, prawda? Jak to naprawić?
bambus
3
@ bambus Zrezygnowałbym z narzędzi powłoki i w tym momencie szukałem innego rozwiązania.
user9517
2

Zainspirowany odpowiedziami Dennisa Nolte i MikeyB wymyśliłem to rozwiązanie:

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

Działa w oparciu o fakt, że

jeśli ustawiono opcję powłoki failglob i nie znaleziono żadnych dopasowań, drukowany jest komunikat o błędzie i polecenie nie jest wykonywane.

Nawiasem mówiąc, właśnie dlatego przekierowano stderr/dev/null .

Cristian Ciupitu
źródło
1

Zrobiłbym to w Perlu osobiście

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

Powinien zrobić lewę (działa na moim bardzo uproszczonym przypadku testowym).

Sobrique
źródło
0

Oto znaleziona jedna linijka.

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

Edycja : Ups, też nie będzie działać.

Matthew Ife
źródło
To nie działa, ponieważ testujesz nazwy katalogów, a nie plików w nich zawartych.
Cristian Ciupitu
Spróbowałem jeszcze raz. Źle
Matthew Ife
0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q w egrep jest cicho

  • Za pomocą egrepmożesz zamienić regex, którego potrzebujesz

  • ls -1 "{}" wyświetla nazwy plików z polecenia find

sk8asd123
źródło