Рекурсивно искать файлы с определенным расширением

437

Я пытаюсь найти все файлы с определенным расширением в каталоге и его подкаталогах с помощью моего bash (последняя версия Ubuntu LTS).

Вот что написано в файле скрипта:

#!/bin/bash

directory="/home/flip/Desktop"
suffix="in"

browsefolders ()
  for i in "$1"/*; 
  do
    echo "dir :$directory"
    echo "filename: $i"
    #   echo ${i#*.}
    extension=`echo "$i" | cut -d'.' -f2`
    echo "Erweiterung $extension"
    if     [ -f "$i" ]; then        

        if [ $extension == $suffix ]; then
            echo "$i ends with $in"

        else
            echo "$i does NOT end with $in"
        fi
    elif [ -d "$i" ]; then  
    browsefolders "$i"
    fi
  done
}
browsefolders  "$directory"

К сожалению, когда я запускаю этот скрипт в терминале, он говорит:

[: 29: in: unexpected operator

$extensionвместо 'in')

Что здесь происходит, где ошибка? Но эта фигурная скобка

кувырок
источник
2
Ошибка из-за пропавшего '{'
shrewmouse

Ответы:

750
find $directory -type f -name "*.in"

немного короче всего этого (и безопаснее - имеет дело с пробелами в именах файлов и каталогов).

Возможно, ваш скрипт не работает для записей, которые не имеют .в своем имени, делая $extensionпустым.

Мат
источник
16
да, findпо умолчанию рекурсивно Вы можете ограничить глубины, если хотите (см. справочную страницу).
Мат
1
Я хотел бы передать все найденные файлы в качестве аргументов в jar-файл. Как это можно сделать?
перевернуть
8
@flip: это другой вопрос. Опубликуйте новый вопрос, подробно описав, что вы хотели бы сделать и что вы пробовали до сих пор.
Мат
Одно небольшое исправление: используйте «* .in» или \ *. In вместо «* .in», потому что двойные кавычки не мешают расширению оболочки. Т.е. ваш скрипт не будет работать должным образом, если в текущем каталоге есть файл с расширением .in.
Шнацель
4
@Shnatsel: двойные кавычки препятствуют расширению оболочки. Попробуйте это.
Мат
188
find {directory} -type f -name '*.extension'

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

find . -type f -name '*.csv'
Мохаммад АльКаннех
источник
60

Синтаксис, который я использую, немного отличается от предложенного @Matt:

find $directory -type f -name \*.in

(это на одно нажатие клавиши меньше).

Скотт К Уилсон
источник
1
Сценарий Мэтта также не будет работать, если в текущем каталоге есть файл с расширением .in, а ваш будет работать. См stackoverflow.com/questions/5927369/...
Shnatsel
4
@Snatsel этот комментарий (и, следовательно, ваш) совершенно неправильно.
gniourf_gniourf
1
@gniourf_gniourf Вы должны предоставить ссылку на свое утверждение, иначе можно просто возразить: «Нет, вы не правы». Но на самом деле вы правы: gnu.org/software/bash/manual/html_node/Double-Quotes.html
Murmel
@ user1885518: Я думаю, что это должен быть парень, который утверждает, что скрипт не работает, который должен предоставить некоторые примеры, где скрипт не работает. Это то, что я делаю, когда я оставляю комментарии, где есть неработающие скрипты: обычно это кавычки и имена файлов, содержащие пробелы, переводы строк, глобусы и т. Д., И я специально объясняю, почему он не работает.
gniourf_gniourf
2
Предоставление ссылки - это всегда хороший способ обсуждения, оно не зависит от того, кто был первым. Он должен, ты должен.
Мурмель
14

Без использования find:

du -a $directory | awk '{print $2}' | grep '\.in$'
РТРС
источник
3
В grepэтом нет необходимости. awkимеет регулярные выражения и может ограничивать его вывод значениями, соответствующими шаблону.
Kenster
Этот метод чрезвычайно полезен, если вы проходите сотни терабайт. Команда поиска занимает слишком много времени для обработки. Это начинается немедленно.
Протонова
1
awk|grepэто анти-шаблон. Пусть awk сделает помойку.
Дженс
10
  1. Там {пропал послеbrowsefolders ()
  2. Все $inдолжно быть$suffix
  3. Строка cutполучит только среднюю часть front.middle.extension. Вы должны прочитать руководство по оболочке ${varname%%pattern}и друзей.

Я предполагаю, что вы делаете это как упражнение в написании сценариев оболочки, в противном случае findуже предложенное решение - это путь.

Для проверки правильности синтаксиса оболочки без запуска скрипта используйте sh -n scriptname.

Jens
источник
7

Хотя использование findкоманды здесь может быть полезным, сама оболочка предоставляет опции для выполнения этого требования без каких-либо сторонних инструментов. bashОболочка обеспечивает расширенный вариант поддержки Glob с помощью которой вы можете получить имена файлов при рекурсивных путях, матч с расширениями , которые вы хотите.

Расширенная опция - это то, extglobчто нужно установить, используя shoptопцию, как показано ниже. Опции включены с -sподдержкой и отключены с -uфлагом. Кроме того, вы можете использовать несколько опций, то есть, nullglobкогда непревзойденный глобус будет полностью удален, заменен набором нулевых слов. И globstarэто позволяет проходить через все каталоги

shopt -s extglob nullglob globstar

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

Например, чтобы перечислить все *.csvфайлы в рекурсивных путях

fileList=(**/*.csv)

Опция **состоит в том, чтобы проходить по подпапкам и *.csvрасширять глобально, чтобы включить любой файл упомянутых расширений. Теперь для печати реальных файлов, просто сделайте

printf '%s\n' "${fileList[@]}"

Использование массива и правильное раскрытие в кавычках - правильный путь при использовании в сценариях оболочки, но для интерактивного использования вы можете просто использовать lsвыражение glob как

ls -1 -- **/*.csv

Это вполне может быть расширено для соответствия нескольким файлам, т.е. файлам, заканчивающимся несколькими расширениями (то есть аналогично добавлению нескольких флагов в findкоманду) Например, рассмотрим случай получения всех файлов рекурсивных изображений, например расширений *.gif, *.pngи *.jpgвсе, что вам нужно, это

ls -1 -- **/+(*.jpg|*.gif|*.png)

Это вполне может быть расширено, чтобы иметь отрицательные результаты также. С тем же синтаксисом можно использовать результаты glob для исключения файлов определенного типа. Предположим, вы хотите исключить имена файлов с расширениями выше, вы можете сделать

excludeResults=()
excludeResults=(**/!(*.jpg|*.gif|*.png))
printf '%s\n' "${excludeResults[@]}"

Конструкция !()представляет собой операцию отрицания, которая не включает в себя ни одно из расширений файлов, перечисленных внутри, и |является оператором чередования, аналогичным тому, который используется в библиотеке расширенных регулярных выражений для сопоставления ИЛИ глобанов.

Обратите внимание, что эта расширенная поддержка glob недоступна в оболочке POSIX bourne и является чисто специфической для последних версий bash. Так что, если вы рассматриваете переносимость скриптов, работающих в POSIX и bashоболочках, эта опция не будет правильной.

Inian
источник
6

Чтобы найти все pom.xmlфайлы в вашем текущем каталоге и распечатать их, вы можете использовать:

find . -name 'pom.xml' -print
Бхарат Ядав
источник
1
find $directory -type f -name "*.in"|grep $substring
Серджиу
источник
0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 
Авинаш Кумар Мишра
источник
1
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.
Роллштюльфахрер