Знайдіть каталоги, де відсутні файли з конкретним закінченням


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
Однокласник без тимчасових файлів, якщо використовується оболонка Bash: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: Правда: команда find є хиткою та не дуже перевіреною. Розуміння генератора - так, це ще один приємний спосіб зробити це.
MikeyB

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

3

Оболонка розширює *, але у вашому випадку немає жодної оболонки, а лише тестова команда, виконана методом 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) '. Насправді це його бібліотека калібрів.
bamboo

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

Це, здається, працює для мене з простими а.ENDING іменами
користувачем9517

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

3
@bamboo Я б відмовився від утиліти оболонки і шукав би інше рішення на даний момент.
користувач9517

2

Надихнувшись відповідями Деніса Нолте та MikeyB , я придумав таке рішення:

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

Це працює виходячи з того, що

якщо встановлено параметр оболонки failglob , і не знайдено збігів, друкується повідомлення про помилку і команда не виконується.

До речі, саме тому stderr було перенаправлено на /dev/null.


1

Я б робив це в перл особисто

#!/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 "{}" виводить імена файлів із команди find

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.