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?


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 

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

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


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 .


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).


0

Oto znaleziona jedna linijka.

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

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


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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.