Base Linux для користувача. Команда find як файловий апофігей, Linux, Операційні системи, статті

Олексій Федорчук

У цій замітці мова піде про набір исходников, відомому в проекті GNU як findutils. І в першу голову – про команду find (як, втім, і про тісно пов’язаної з нею команді xargs). Настільки висока честь випадає їм тому, що за допомогою цих двох команд можна вирішити якщо не всі, то більшість (Buono Parte) проблем, що виникають при роботі з файлами.

Крім find і xargs, до складу набору findutils входять також команди locate і updatedb, про які говорилося раніше, а також команди bigram, code, frcode, реального програми яких я, чесно кажучи, не знаю (і, відповідно, говорити про них не буду).

Отже, файловий апофігей – команда find. Строго кажучи, всупереч своєму імені, команда ця виконує не пошук файлів, як такої, але – рекурсивний обхід дерева каталогів, починаючи з заданого в якості аргументу, відбирає з них файли у відповідність з деякими критеріями і виконує над відбракованих файловим господарством деякі дії. Саме цю її особливість підкреслює резюме команди find, що отримується (У деяких системах) за допомогою
$ whatis find
find(1) – walk a file hierarchy

що стосовно випадку можна перевести як “прогулянка по файловій системі”. Команда find за своїм синтаксису істотно відрізняється від більшості інших Unix-команд. В узагальненому вигляді формат її можна представити таким чином:
$ Find аргумент [опція_поіска] [значення] [опція_действія]
Аргумент – це шлях пошуку, тобто каталог, починаючи з якого слід шукати файли, наприклад, кореневої

$ Find / [опція_поіска] [значення] [опція_действія]

або домашній каталог користувача

$ Find ~ / [опція_поіска] [значення] [опція_действія]

Опція пошуку – критерій, за яким слід шукати файл (файли). В якості таких можуть виступати ім’я файлу (-name), його тип (-type), атрибути приналежності, доступу або часу.
Ну а опція дії визначає, що ж належить зробити з знайденим файлом або файлами. А зробити з ними, треба зауважити, можна чимало – починаючи з виведення на екран (-print) і кінчаючи передачі в якості аргументів будь-який інший команді (-exec).
Як можна помітити, опція пошуку і опція дії передує знаком дефіса, значення першої відокремлюється від її імені пропуском.
Однак почнемо по порядку. Опції пошуку команди find дозволяють виконати вищезазначений пошук за такими критеріями (символ дефіса перед опціями нижче опущений, але не слід забувати його ставити):


Крім цього, існує ще одна опція пошуку – fstype, приписуюча виконувати пошук тільки в файловій системі зазначеного типу; очевидно, що вона може поєднуватися з будь-якими іншими опціями пошуку. Наприклад, команда

$ find / -fstype ext3 -name zsh*

буде шукати файли, що мають відношення до оболонки Z-Shell, починаючи з кореня, але тільки – в межах тих розділів, на яких розміщена файлова система Ext3fs (на моїй машині – це саме чистий корінь, за вирахуванням каталогів / usr, / opt, / Var, / tmp і, звичайно ж, / home.


Критерії відбору файлів можуть групуватися практично будь-яким чином. Так, у формі

$find ~/ -name *.tar.gz newer filename

вона вибере в домашньому каталозі користувача всі компрессированний архіви, створені після файла з ім’ям filename. За замовчуванням між критеріями відбору передбачається наявність логічного оператора “І”. Тобто будуть шукатися файли, що задовольняють і масці імені, і відповідному атрибуту часу. Якщо потрібне використання оператора “АБО”, він повинен бути явно визначено у вигляді додаткової опції-o між опціями пошуку. Так, команда:

$find ~/ -mtime -2 -o newer filename

покликана відібрати файли, створені менш двох діб тому, або ж – пізніше, ніж файл filename.


Особливість GNU-реалізації команди find (як, втім, і її тезки з числа BSD-утиліт) – те, що вона за замовчуванням виводить список відібраних відповідно до заданих критеріїв файлів на екран, не вимагаючи додаткових опцій дії. Однак, як кажуть, в інших Unix-системах (пам’ятається, навіть і в деяких реалізаціях Linux) зазначення будь-якої з таких опцій – обов’язково. Так що розглянемо їх по порядку.


Для виведення списку відібраних файлів на екран у загальному випадку призначена опція-print. Висновок цей має приблизно такий вигляд:

find . -name f* -print
./file1
./file2
./dir1/file3

Схожий зміст має і опція-ls, проте вона виводить більш повні відомості про знайдені файли, аналогічно команді ls з опціями-dgils:

$ find / -fstype ext3 -name zsh -ls 88161 511-rwxr-xr-x 1 root root 519320 23 листопада 15:50 / bin / zsh

Важливе, як мені здається, зауваження. Якщо команда зазначеного виду буде дана від імені звичайного користувача (не root-оператора), крім наведеної вище рядки виводу, підуть численні повідомлення на зразок

find: /root: Permission denied

вказують на каталоги, закриті для перегляду звичайним користувачем, і дуже заважають сприйняттю. Щоб придушити їх, слід перенаправити висновок повідомлення про помилки в файл / Dev / null, тобто вказати їм “Дорогу нікуди”:

$ find / -fstype ext3 -name zsh -ls 2> /dev/null

Йдемо далі. Опція-delete знищить всі файли, відібрані по зазначеним критеріям. Так, командою

$ find ~ -atime +100 -delete

будуть автоматично стерті всі файли, до яких не було звернення за останні 100 днів (з мовчазної припущення, що раз до них три місяці не зверталися – значить, вони і взагалі не потрібні). Винищенню піддадуться файли в підкаталогах будь-якого рівня вкладеності – але не включають їх підкаталоги (Якщо, звичайно, останні самі не підпадають під критерії відбору).


І, нарешті, опція-exec – саме нею обумовлено велич утиліти find. Як значення її можна вказати будь-яку команду з необхідними опціями – і вона буде виконана над відібраними файлами, які будуть розглядатися в якості її аргументів. Проілюструємо це на прикладі.


Використовувати опцію-delete, як ми це тільки що зробили – не саме здорове рішення, бо файли при цьому видаляються без запиту, і можна випадково видалити небудь потрібне. І тому досягнемо тієї ж мети наступним чином:

$ find ~/ -atime +100 -exec rm -i {} ;

У цьому випадку на видалення кожного відібраного файлу буде запитуватися підтвердження.


Звертаю увагу на послідовність символів {}; (з пробілом між закриває фігурною дужкою і зворотним слешем) в кінці рядка. Пара фігурних дужок {} символізує, що свої аргументи виконується команда (у прикладі – rm) отримує від результатів відбору команди find, крапка з комою означає завершення рядка (без неї на натискання Enter послідувало б вторинне запрошення командного рядка), а зворотний слеш екранує її спеціальне значення.


Крім опції дії-exec, у команди find є ще одна, близька за змістом, опція – ok. Вона також викликає якусь довільну команду, якій в якості аргументів передаються імена файлів, відібрані за критеріями, заданим опцією (Опціями) пошуку. Однак перед виконанням кожної операції над кожним файлом запитується підтвердження.


Наведений приклад, хоча й цілком життєвий, достатньо елементарний. Розглянемо більш складний випадок – збирання в один каталог усіх скріншотів у форматі PNG, розкиданих по древу домашнього каталогу:

$ find ~/ -name *.png -exec cp {} imagesdir ;

В результаті всі png-файли будуть вишукані і скопійовані (Або – переміщені, якщо скористатися командою mv замість cp) в одне місце.


А тепер – варіант вирішення завдання, яка здавалася мені спочатку важко вирішуваною: рекурсивне привласнення необхідних атрибутів доступу в розгалуженому дереві каталогів – різних для регулярних файлів і каталогів.


Навіщо і чому це потрібно? Поясню на прикладі. Якось раз, обзавівшись величезним (40 Гбайт) вінчестером, я вирішив зібрати на нього всі потрібні мені дані, розсіяні по дисках CD-R/RW (Сумарним обсягом з півкубометра) і декільком змінним вінчестерам, одні з яких були відформатовані в FAT16, інші – в FAT32, треті – взагалі в ext2fs (до слова сказати, робочої моєї системою в той момент була FreeBSD). Вивантажив все це богачество в один каталог на новому диску, я створив у ньому досить непривабливу картину.


Ну, по-перше, всі файли, скопійовані з CD і FAT-дисків, отримали біти наповнюваності, хоча все це були файли даних. Здавалося б, дрібниця, але іноді дуже заважає; в деяких системах це не дозволяє, наприклад, переглянути html-файл в Midnight Commander простим натисканням Enter. По-друге, для деяких каталогів, навпаки, виконання не було передбачено ні для кого – тобто я ж сам перейти в них не міг. По-третє, каталоги (і файли) з CD часто не мали атрибута зміни – а вони потрібні мені були для роботи (в т.ч. і редагування). Скінчено, від усіх цих артефактів можна було б позбутися, передбачивши належні опції монтування накопичувачів (кожного накопичувача – а їх число, повторюю, вимірювалося вже обсягом займаного простору), та я про це і не подумав – що виросло, то виросло. Так що ситуація явно вимагала виправлення, однак проробити вручну таку роботу над даними більш ніж в 20 Гбайт віділось немислимим.


Та так воно, власне, і було б, якщо б не опція-exec утиліти find. Яка дозволила змінити права доступу потрібним чином. Отже, спочатку відбираємо всі регулярні файли і знімаємо з них біт виконання для всіх, заодно привласнюючи атрибут зміни для себе, коханого:

$ find ~/dir_data -type f -exec chmod a-x,u+w {} ;

Далі – пошук каталогів і зворотна процедура над підсумковій вибіркою:

$ find ~/dir_data -type d -exec chmod a+xr,u+w {} ;

І справа – в капелюсі, всі права доступу стали однаковими (і тими, що мені потрібні). Саме після цього випадку я, подібно митьковской Максиму, перейнявся величчю філософії марксизму (Пардон, утиліти find). Але ж це ще не межа її можливостей – останній встановлюється тільки встають завданнями та власною фантазією …


Так, за допомогою команди find легко налагодити періодичне архівування результатів поточної роботи. Для цього насамперед створюємо командою tar повний архів результатів своєї життєдіяльності:

tar cvf alldata.tar ~/*

А потім в міру своєї зіпсованості (або, навпаки, акуратності), час від часу запускаємо команду

$find ~/ -newer alldata.tar -exec tar uvf alldata.tar {} ;

Ще один практично корисний варіант використання команди find в мирних цілях – періодичне додавання окремо написаних фрагментів до підсумкового праці життя (наприклад, мемуарам енікейщіка). Втім, щоб зробити це, необхідно спочатку ознайомитися з командами обробки файлів, до яких ми незабаром звернемося.


А поки – про обмеження можливостей настільки чудовою зчіпки команди find з опцією дії-exec (Поширюються і на опцію-ok). Воно досить очевидно: викликається будь-який з цих опцій команда виконується в рамках самостійного процесу, що на слабких машинах, як кажуть, призводить до падіння продуктивності (мушу зауважити, що на машинах сучасних помітити цього практично неможливо).


У джерелах мені зустрічалося вказівку на ще одне обмеження зв’язки find з опцією-exec – обмеження на довжину командного рядка. Однак на практиці з таким обмеженням мені зіткнутися не доводилося (навіть у штучно створених ситуаціях, типу пошуку за шаблоном s * і тому подібним). І в документації ніяких відомостей з цього приводу я не знайшов.


Тим не менш, якщо яка-небудь з цих проблем виникне – вона цілком переборна. І зробити це покликана команда xargs. Вона визначається як будівник і виконавець командної рядки зі стандартного вводу. А оскільки на стандартний ввід може бути спрямований висновок команди find – xargs сприйме результати її роботи як аргументи якої-небудь команди, яку в свою чергу, можна розглядати як аргумент її саме (по замовчуванням такою командою-аргументом є / bin / echo).


Повторюся – в реальності не бачу необхідності (для себе особисто) в команді xargs і тому не займався її вивченням. Так що зацікавлених відсилаю до відповідної man-сторінці (і – Бібліографії в наступному абзаці).


Бібліографія


Прекрасне опис роботи утиліти find (і xargs, заодно), правда, в BSD-реалізації, – в парі статей Dru Lavigne, розміщених в Софтерре в перекладах Станіслава Лапшанского: частина 1 і частина 2

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*