Получить список подкаталогов, содержащих файл, соответствующий строке

Как я могу получить список подкаталогов, содержащих файл, соответствующий определенной строке?

Более конкретно, я ищу каталоги, содержащие файл с буквой «f», где-то встречающийся в имени файла.

В идеале, список не будет иметь дубликатов и содержать только путь без имени файла.

  • Как исключить папки proc и sys из поиска с помощью команды find?
  • Как искать файлы по каталогу и имени файла combo pattern
  • В чем разница между поиском. и найти . -Распечатать
  • как сделать поиск текста на терминале напрямую
  • Как найти все файлы JPG в файловой системе, когда расширение .jpg не является обязательным?
  • Как я могу запустить определенную команду для каждого результата поиска?
  • apropos regex начать с?
  • Как найти файл в каталоге
  • 5 Solutions collect form web for “Получить список подкаталогов, содержащих файл, соответствующий строке”

    find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort |uniq 

    Вышеприведенное находит все файлы под текущим каталогом ( . ), -type f являются обычными файлами ( -type f ) и имеют f где-то в их имени ( -name '*f*' ). Затем sed удаляет имя файла, оставляя только имя каталога. Затем список каталогов сортируется ( sort ) и дубликаты удаляются ( uniq ).

    Команда sed состоит из одной замены. Он ищет совпадения с регулярным выражением /[^/]+$ и заменяет все, что соответствует ничто. Знак доллара означает конец строки. [^/]+' означает один или несколько символов, которые не являются косой чертой. Таким образом, /[^/]+$ означает, что все символы из последней косой черты до конца строки. Другими словами, это соответствует имени файла в конце полного пути. Таким образом, команда sed удаляет имя файла, оставляя неизменным имя каталога, в котором находился файл.

    Упрощения

    Многие современные команды sort поддерживают флаг -u который делает uniq ненужным:

     find . -type f -name '*f*' | sed -r 's|/[^/]+$||' |sort -u 

    Кроме того, если ваша команда find поддерживает его, возможно, вы можете напрямую распечатать имена каталогов. Это позволяет избежать необходимости sed :

     find . -type f -name '*f*' -printf '%h\n' | sort -u 

    Более надежная версия (требуется инструмент GNU)

    Вышеупомянутые версии будут смущены именами файлов, которые включают в себя новые строки. Более надежным решением является сортировка по строкам с nul-terminated:

     find . -type f -name '*f*' -printf '%h\0' | sort -zu | sed -z 's/$/\n/' 

    Почему бы не попробовать это:

     find / -name '*f*' -printf "%h\n" | sort -u 

    Для этого вы можете использовать 2 метода. Один будет разбирать строку, а другой будет работать с каждым файлом. Разбор строки использует такой инструмент, как grep , sed или awk , очевидно, будет быстрее, но вот пример, показывающий оба, а также то, как вы можете «профилировать» 2 метода.

    Пример данных

    В приведенных ниже примерах мы будем использовать следующие данные

     $ touch dir{1..3}/dir{100..112}/file{1..5} $ touch dir{1..3}/dir{100..112}/nile{1..5} $ touch dir{1..3}/dir{100..112}/knife{1..5} 

    Удалите некоторые из *f* файлов из dir1/* :

     $ rm dir1/dir10{0..2}/*f* 

    Подход №1 – Анализ по строкам

    Здесь мы будем использовать следующие инструменты: find , grep и sort .

     $ find . -type f -name '*f*' | grep -o "\(.*\)/" | sort -u | head -5 ./dir1/dir103/ ./dir1/dir104/ ./dir1/dir105/ ./dir1/dir106/ ./dir1/dir107/ 

    Подход №2 – Анализ с использованием файлов

    Такая же цепочка инструментов, как и раньше, за исключением этого времени, мы будем использовать dirname вместо grep .

     $ find . -type f -name '*f*' -exec dirname {} \; | sort -u | head -5 ./dir1/dir103 ./dir1/dir104 ./dir1/dir105 ./dir1/dir106 ./dir1/dir107 

    ПРИМЕЧАНИЕ. В приведенных выше примерах используется head -5 чтобы просто ограничить объем вывода, с которым мы имеем дело для этих примеров. Их обычно удаляют, чтобы получить полный список!

    Сравнение результатов

    Мы можем использовать time чтобы взглянуть на два подхода.

    имя_директории

     real 0m0.372s user 0m0.028s sys 0m0.106s 

    Grep

     real 0m0.012s user 0m0.009s sys 0m0.007s 

    Поэтому всегда лучше иметь дело со строками, если это возможно.

    Альтернативные методы анализа строк

    grep & PCRE

     $ find . -type f -name '*f*' | grep -oP '^.*(?=/)' | sort -u 

    СЕПГ

     $ find . -type f -name '*f*' | sed 's#/[^/]*$##' | sort -u 

    AWK

     $ find . -type f -name '*f*' | awk -F'/[^/]*$' '{print $1}' | sort -u 

    Вот один, который я нахожу полезным:

     find . -type f -name "*somefile*" | xargs dirname | sort | uniq 

    Этот ответ бесстыдно основан на ответе slm. Это был интересный подход, но он имеет ограничение, если имена файлов и / или каталогов имеют специальные символы (пробел, полуколономер …). Хорошей привычкой является использование find /somewhere -print0 | xargs -0 someprogam find /somewhere -print0 | xargs -0 someprogam .

    Пример данных

    В приведенных ниже примерах мы будем использовать следующие данные

     mkdir -p dir{1..3}/dir\ {100..112} touch dir{1..3}/dir\ {100..112}/nile{1..5} touch dir{1..3}/dir\ {100..112}/file{1..5} touch dir{1..3}/dir\ {100..112}/kni\ fe{1..5} 

    Удалите некоторые из *f* файлов из dir1/*/ :

     rm dir1/dir\ 10{0..2}/*f* 

    Подход №1 – Анализ с использованием файлов

     $ find -type f -name '*f*' -print0 | sed -e 's#/[^/]*\x00#\x00#g' | sort -zu | xargs -0 -n1 echo | head -n5 ./dir1/dir 103 ./dir1/dir 104 ./dir1/dir 105 ./dir1/dir 106 ./dir1/dir 107 

    ПРИМЕЧАНИЕ . В приведенных выше примерах используется head -5 чтобы просто ограничить объем вывода, с которым мы имеем дело для этих примеров. Их обычно удаляют, чтобы получить полный список! также замените echo которое любая команда вы хотите использовать.

    Linux и Unix - лучшая ОС в мире.