Рекурсивно шукайте файли із конкретним розширенням


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
@Shnatsel цей коментар (а значить, і ваш) явно неправильний.
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має регулярні вирази і може обмежити його вихід значеннями, що відповідають шаблону.
Кенстер

Цей метод є надзвичайно корисним, якщо ви переживаєте 100s терабайт. Команда Find займає занадто багато часу для обробки. Це починається негайно.
Протонова

1
awk|grepє анти-зразком. Нехай awk зробить поздоровлення.
Єнс

10
  1. Пропав {безвістиbrowsefolders ()
  2. Все $inмає бути$suffix
  3. Рядок з cutотримує у вас лише середню частину front.middle.extension. Ви повинні прочитати посібник із оболонок ${varname%%pattern}та друзів.

Я припускаю, що ви робите це як вправу в сценарії оболонок, інакше findвже запропоноване рішення - це шлях.

Щоб перевірити правильність синтаксису оболонки, не запускаючи сценарій, використовуйте sh -n scriptname.



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)

Це дуже добре розширити, щоб мати і негативні результати. З тим самим синтаксисом можна використовувати результати глобуса для виключення файлів певного типу. Припустимо, ви хочете виключити імена файлів із розширеннями вище, ви могли б зробити

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

Конструкція !()- це заперечна операція, щоб не включати жодне з розширень файлів, перелічених всередині та| є оператором чергування так само, як використовується в розширеній бібліотеці регулярних виразів, щоб виконати відповідність АБО глобусів.

Зауважте, що ця розширена глобальна підтримка недоступна в оболонці bourne POSIX, і вона суто специфічна для останніх версій bash. Тож якщо ви розглядаєте можливість переносимості сценаріїв, що працюють через POSIX та bashоболонки, ця опція не буде правильною.


6

Щоб знайти усі pom.xmlфайли у вашому поточному каталозі та роздрукувати їх, ви можете скористатися:

find . -name 'pom.xml' -print


0
for file in "${LOCATION_VAR}"/*.zip
do
  echo "$file"
done 

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