Найти каталоги, в которых отсутствуют файлы с определенным окончанием


10

Я хочу отобразить все каталоги, которые не содержат файлы с определенным окончанием файла. Поэтому я попытался использовать следующий код:

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

В этом примере я хотел отобразить все каталоги, которые не содержат файлы с окончанием .ENDING, но это не работает.

Где моя ошибка?


Это не дубликат каталогов List, не содержащих файл, поскольку этот вопрос не относится к групповым символам.
Кристиан Чиупиту

Ответы:


7

Вот решение в три этапа:

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
comm -3 <(find . -type f -name \*ENDING -exec dirname {} \; |sort -u) <(find . -type d |sort -u)
Однострочник

4

Вот так!

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

Или поочередно (и более питонично):

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

Бонус DLC Контент!

Исправленная (в основном) версия команды find:

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

Я думаю, что найти решения не удастся, test: ...: binary operator expectedесли *.ENDINGв каталоге несколько файлов.
Кристиан Чиупиту

next(filter(lambda x: x.endswith('.ENDING'), filenames))также могут быть написаны с использованием генератора понимания , т.е. next(x for x in filenames if x.endswith('.ENDING')).
Кристиан Чиупиту

@CristianCiupitu: Правда: команда поиска является хакерской и не полностью протестирована. Генератор понимания - да, это еще один хороший способ сделать это.
MikeyB

1
Я думаю, что еще более удачным решением было бы использование, if not any(x.endswith('.ENDING') for x in filenames)основанное на том факте, что любой возврат Falseдля пустой итерации.
Кристиан Чиупиту

3

Оболочка расширяет *, но в вашем случае оболочка не задействована, только команда test, выполняемая find . Следовательно, файл, существование которого проверено, буквально назван *.ENDING.

Вместо этого вы должны использовать что-то вроде этого:

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

Это приведет к расширению sh*.ENDING при выполнении теста .

Источник: найти глобус на UX.SE


Не работает. Я получаю следующую ошибку: sh: -c: line 0: syntax error near unexpected token ('`. Мои имена каталогов имеют формат' xyz (dfdf) '. На самом деле это библиотека калибров.
бамбук,

1
Когда я пытаюсь это сделать, я получаю sh: line 0: test: foo1/bar.ENDING: binary operator expectedкаталог, содержащий файл с окончанием ENDING.
Дженни Д

Кажется, это работает для меня с простыми именами файлов
a.ENDING

У меня есть следующая структура каталогов test: test-> test (test) -> test.ending После использования этого кода я получаю sh: -c: line 0: syntax error near unexpected token ('sh: -c: строка 0:' test -e test (test) / *. End '). / тест (тест) `Но когда я изменяю .ending к .xyz я получаю тот же результат Это потому , что у меня есть прицветники как имя каталога, право Как я могу исправить это..?
бамбук

3
@bamboo Я бы отказался от утилит оболочки и искал бы другое решение.
user9517

2

Вдохновленный ответами Денниса Нольте и MikeyB , я придумал это решение:

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

Работает исходя из того что

если установлена опция оболочки failglob и совпадений не найдено, выводится сообщение об ошибке и команда не выполняется.

Кстати, именно поэтому stderr был перенаправлен на /dev/null.


1

Я бы сделал это в 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, "." );

Должен сделать свое дело (работает на моем очень упрощенном тестовом примере).


0

Вот находка с одним вкладышем.

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

Изменить : Ой, не будет работать.


Это не работает, потому что вы проверяете имена каталогов, а не файлов внутри них.
Кристиан Чиупиту

Я дал ему еще одну попытку. Плохо
Мэтью Ифе

0
find . -type d '!' -exec sh -c 'ls -1 "{}"|egrep -q "*ENDING$"' ';' -print
  • q в егрепе для тишины

  • С помощью которого egrepвы можете поменять регулярное выражение, которое вам нужно

  • ls -1 "{}" выводит имена файлов из команды поиска

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.