1. Операції з файлами і фільтрація

Основна частина роботи у командному рядку ведеться з файлами. У цьому розділі ми розглянемо, як переглядати і фільтрувати вміст файлів, видобувати з файлів потрібну інформацію за допомогою однієї команди і сортувати вміст файла.

1.1. cat, tail, head, tee: Команди для виведення вмісту файлів

Ці команди мають майже однаковий синтаксис: ім'я_команди [опції] [файл(и)], і можуть бути використані в каналах. Всі вони використовуються для виведення частини файла відповідно до певних критеріїв.

Утиліта cat об'єднує файли і виводить результат на стандартний вихід, яким зазвичай є екран вашого комп'ютера. Це одна з найчастіше використовуваних команд. Наприклад, ви можете скористатися:

# cat /var/log/mail/info

для виведення вмісту файла журналу поштового демона на стандартний вихід[23]. Команда cat має дуже корисну опцію (-n), яка дозволяє вам виводити номери рядків.

Деякі файли, на зразок журналів демонів (якщо вони запущені), зазвичай мають досить великий розмір[24] і повне їх виведення на екран буде не дуже корисним. Взагалі, вам потрібні лише кілька рядків з файла. Для цього ви можете скористатися командою tail. Наступна команда виведе (за умовчанням) останні 10 рядків з файла /var/log/mail/ info:

# tail /var/log/mail/info

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

# tail -f /var/log/mail/info

В цьому випадку всі зміни у файлі /var/log/mail/info будуть негайно виводитися на екран. Використання команди tail з опцією -f дуже корисно, коли потрібно знати, як працює ваша система. Наприклад, спостерігаючи за файлом журналу /var/ log/messages, ви зможете завжди отримувати оновлену інформацію про системні повідомлення і демонів.

Якщо ви використовуєте tail для кількох файлів, вона буде виводити в окремому рядку імена цих файлів перед виведенням їх вмісту. Робота з опцією -f при цьому також можлива, що є цінним доповненням для спостереження за взаємодією різних частин системи.

Ви можете використовувати опцію -n для виведення останніх n рядків файла. Наприклад, для виведення останніх 2-х рядків наберіть:

# tail -n2 /var/log/mail/info

Як і для інших команд, ви можете одночасно використовувати різні опції. Наприклад, при одночасному використанні опцій -n2 і -f ви почнете з двох останніх рядків файла і продовжуватимете спостерігати появу нових рядків у міру додавання їх у файл журналу.

Команда head схожа на tail, але вона виводить перші рядки файла. Наступна команда виведе (за умовчанням) перші 10 рядків файла /var/log/mail/info:

# head /var/log/mail/info

Як і у випадку з tail ви можете скористатися опцією -n для вказівки кількості рядків для виведення. Наприклад, для виведення перших 2-х наберіть:

# head -n2 /var/log/mail/info

Також ви можете використовувати ці дві команди спільно. Наприклад, якщо ви хочете побачити лише рядки 9 і 10, ви можете скористатися командою, у якій head вибере перші 10 рядків з файла і передасть їх по каналу до команди tail.

# head /var/log/mail/info | tail -n2

При цьому остання команда візьме останні 2 рядки і виведе їх на екран. Таким же чином ви можете вибрати 20-й від кінця файла рядок:

# tail -n20 /var/log/mail/info |head -n1

У цьому прикладі ми говоримо команді tail взяти останні 20 рядків й передати їх по каналу в head. Потім команда head виводить на екран перший рядок з отриманих даних.

Давайте припустимо, що нам потрібно вивести на екран результат останнього прикладу і зберегти його у файл results.txt. Нам може допомогти утиліта tee. Її синтаксис:

tee [опції] [файл]

Тепер ми можемо змінити попередню команду так:

# tail -n20 /var/log/mail/info |head -n1|tee results.txt

Давайте розглянемо ще один приклад. Нам потрібно вибрати останні 20 рядків, зберегти їх у файлі results.txt, а на екран вивести лише перший з них. Тоді ми повинні запровадити наступне:

# tail -n20 /var/log/mail/info |tee results.txt |head -n1

У команди tee є корисна опція (-a), яка дозволяє вам дописати дані в кінець існуючого файла.

У наступному розділі ми побачимо, як ми можемо використовувати команду grep як фільтр для відділення повідомлень Postfix від повідомлень інших служб.

1.2. grep: Пошук рядків у файлах

Ні ім'я команди, ні її абревіатура (“General Regular Expression Parser” - синтаксичний аналізатор загальних регулярних виразів) не є інтуїтивними, проте її дія і використання досить прості: grep шукає в одному кількох файлах шаблон, який задано в якості аргументу. Її синтаксис:

grep [опції] <шаблон> [один чи кілька файлів]

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

  • -i: пошук без урахування регістра (тобто ігнорування різниці між верхнім і нижнім регістром);

  • -v: зворотній пошук. Виведення рядків, які не відповідають шаблону;

  • -n: виведення номеру рядка для кожного з знайдених рядків;

  • -w: повідомляє grep'у, що шаблон має збігатися з усім словом.

Отже, давайте тепер повернемося до аналізу лог-файла поштового демона. Нам необхідно знайти всі рядки у файлі /var/log/mail/info, що містять шаблон “postfix”. Для цього ми вводимо таку команду:

# grep postfix /var/log/mail/info

Якщо нам потрібно знайти всі рядки, що НЕ містять шаблону postfix, нам необхідно скористатися опцією -v:

# grep -v postfix /var/log/mail/info

Команду grep можна використовувати в каналах.

Припустимо, що нам потрібно знайти всі повідомлення про успішно відправлені листи. У цьому разі ми повинні відфільтрувати всі рядки, додані поштовим демоном в файл журналу (ті, що містять шаблон postfix), і вони повинні утримувати повідомлення про успішну відправку (status=sent)[25]:

# grep postfix /var/log/mail/info |grep status=sent

У цьому разі команда grep використана двічі. Це дозволяється, але виглядає не зовсім гарно. Той самий результат може бути отримано за допомогою утиліти fgrep. По суті fgrep - це простіший спосіб для виклику grep -F. Спочатку нам потрібно створити файл, який містить шаблони, записані кожен в окремому рядку. Такий файл може бути створено так (ми використовуємо patterns.txt як ім'я файлу):

# echo -e 'status=sent\npostfix' >./patterns.txt

Перевірте результат командою cat. \n - це спеціальний шаблон, означає “новий рядок”.

Потім ми викликаємо наступну команду, у якій ми використовуємо файл patterns.txt і утиліту fgrep замість “подвійного виклику” команди grep:

# fgrep -f ./patterns.txt /var/log/mail/info

Файл ./patterns.txt може містити скільки завгодно шаблонів. Наприклад, для вибору повідомлень про листи, успішно відправлених на адресу peter@mandriva.com, достатньо буде додати цю електронну адресу в наш файл ./patterns.txt, виконавши таку команду:

# echo 'peter@mandriva.com' >>./patterns.txt

Ясна річ, що ви можете комбінувати команду grep з tail і head. Якщо нам потрібно знайти повідомлення про передостанній електронний лист, що відправлений на адресу peter@mandriva.com, ми використовуємо:

# fgrep -f ./patterns.txt /var/log/mail/info | tail -n2 | head -n1

Тут ми застосували описаний вище фільтр і відправили результат через канал в команди tail і head. Вони вибрали з даних передостаннє значення.

1.3. egrep: Регулярні вирази та фільтрування

З допомогою grep ми обмежені шаблонами і фіксованими даними. Як нам знайти всі електронні листи, відправлені кожному працівнику “ABC Company”? Пошук у переліку їхніх електронних адрес буде не таким вже й простим завданням, бо ми можемо пропустити когось з них, або нам доведеться вручну копирсатися у файлі журналу.

Як і у випадку зі fgrep, grep має скорочений виклик для команди grep -E: egrep. egrep використовує регулярні вирази замість шаблонів, надаючи нам потужніший інтерфейс для “grep'ання” тексту.

Що ж до того, що згадувалось у розділі Параграф 3, “Шаблони підстановки в командному процесорі” при розгляді шаблонів підстановки, ось кілька додаткових регулярних виразів:

  • [:alnum:], [:alpha:] і [:digit:] можуть бути використані замість визначення класів самих символів і уявляють, відповідно: літери плюс всі цифри, літери (верхній і нижній регістри) і всі цифри. У них є додаткові переваги - вони вони передбачають міжнародні символи і враховують регіональне налаштування системи.

  • [:print:] представляє всі символи, які можуть бути виведені на екран.

  • [:lower:] і [:upper:] представляють усі літери верхнього і нижнього регістрів відповідно.

Існує багато інших доступних класів і ви можете переглянути їх у egrep(1). Перелічені вище класи є найчастіше використовуваними.

Після регулярних виразів можуть слідувати один чи кілька різних операторів, що повторюються:

?

Попередній елемент є необов'язковим, тобто відповідає жодному або єдиному входженню, але не більше.

*

Попередній елемент відповідатиме 0 або кілька входженням.

+

Попередній елемент відповідатиме єдиному або кільком входженням.

{n}

Попередній елемент відповідає рівно n входженням.

{n,}

Попередній елемент відповідає n або кільком входженням.

{n,m}

Попередній елемент відповідає як мінімум n входженням, але не більше m разів.

Якщо ви візьмете регулярний вираз в квадратні дужки, пізніше ви зможете відновити його. Припустимо, що ви зазначили вираз [:alpha:]+. Це може бути слово. Якщо ви хочете визначити двічі повторювані слова, ви можете помістити це словосполучення в дужки і повторно використовувати його за допомогою \1, якщо це перша група. У вас може бути до 9 таких “записів”.

$ echo -e "abc def\nabc abc def\nabc1 abc1\nabcdef\nabcdabcd\nabcdef abcef" > testfile
$ egrep "([[:alpha:]]+) \1" testfile
abc abc def
$
[Примітка]Примітка

Символи [ і ] є частиною імені групи, тому ми повинні включити їх, щоб використовувати цей клас символів. Перший знак [ повідомляє, що використовуватимемо групу символів, друга дужка є частиною імені групи, а потім йдуть відповідні дужки ], що закривають.

Єдиним рядком, якого повернуто, буде рядок, що відповідає виключно двом групам літер, розділених пробілом. Ніяка інша група не є входженням регулярного виразу.

Також ви можете використовувати символ |, який визначає входження для виразу зліва від знака | або для виразу праворуч від цього знака. Цей оператор об'єднує ці вирази. Використовуючи створений раніше файл testfile, ви можете спробувати пошукати вирази, що містять лише слова, що дублюються, або містять слова, що дублюються, з числами:

$ egrep "([[:alpha:]]+) \1|([[:alpha:][:digit:]]+) \2" testfile
abc abc def
abc1 abc1
$

Зверніть увагу, що для другої групи, що використовує дужки, ми повинні використовувати \2, у протилежному випадку висловлення не буде відповідати тому, що нам потрібно. Більш ефективним виразом в окремо взятому разі буде:

$ egrep "([[:alnum:]]+) \1" testfile
abc abc def
abc1 abc1
$

І, на закінчення, для використання певних символів ви повинні їх “заекранувати”, поставивши перед ними зворотну скісну риску. Ось ці символи: ?, +, {, |, (, ) і, звісно ж \. Для використання їх у своїх висловлюваннях ви повинні писати: \?, \+, \{, \|, \( \) і \\.

Ця маленька хитрість може допомогти уникнути повторення набраних слів у “вашому вашому” тексті.

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

1.4. wc: Підрахунок елементів в файлах

Команда wc (Word Count - підрахунок слів) використовується для підрахунку кількості рядків, і символів в файлах. Також вона корисна для обчислення самого довгого рядку. Її синтаксис:

wc [опції] [файл(и)]

Список корисних опцій:

  • -l: виведення кількості рядків;

  • -w: виведення кількості слів;

  • -m: виведення загальної кількості символів;

  • -c: виведення кількості байт;

  • -L: виведення довжини самого довгого рядку у тексті.

За умовчанням команда wc виводить кількість рядків і символів. Ось кілька прикладів використання:

Якщо нам потрібно визначити кількість користувачів у нашій системі, ми можемо запровадити:

$ wc -l /etc/passwd 

Якщо нам потрібно дізнатися кількість CPU у нашій системі, ми пишемо:

$ grep "model name" /proc/cpuinfo |wc -l

У попередньому розділі ми отримали список повідомлень про успішно відправлені листи на адреси, перелічені в нашому файлі ./patterns.txt. Якщо нам потрібно дізнатися кількість цих повідомлень, ми можемо перенаправити наш відфільтрований результат через канал у команду wc:

# fgrep -f ./patterns.txt /var/log/mail/info | wc -l

1.5. sort: Сортування вмісту файла

Нижче представлений синтаксис цієї потужної утиліти для сортування[26]:

sort [опції] [файл(и)]

Давайте відсортуємо частину файла /etc/passwd. Як бачите, цей файл не відсортований:

$ cat /etc/passwd

Якщо нам потрібно відсортувати його за полем login, ми набираємо:

$ sort /etc/passwd

За умовчанням команда sort сортує інформацію за першим полем в порядку зростання (в нашому разі за полем login). Щоб відсортувати дані у порядку спаду, використовуйте опцію -r:

$ sort -r /etc/passwd

Для кожного користувача є свій власний UID, записаний у файлі /etc/passwd. Наступна команда сортує файл в порядку зростання за полем UID:

$ sort /etc/passwd -t":" -k3 -n

Тут ми використовуємо такі опції sort:

  • -t":": повідомляє sort'у, що роздільником полів є символ ":";

  • -k3: означає, що сортування має бути виконано по третій колонці;

  • -n: повідомляє що виконується сортування числових даних, а не алфавітних.

Те ж може бути виконано у зворотному порядку:

$ sort /etc/passwd -t":" -k3 -n -r

Зверніть увагу, що sort має дві важливі опції:

  • -u: суворе сортування: виключаються повторювані поля сортування;

  • -f: ігнорування регістра (рядкові символи обробляються так само, як і прописні).

І, нарешті, якщо ми хочемо знайти користувача з максимальним UID, ми можемо скористатися наступної командою:

$ sort /etc/passwd -t":" -k3 -n |tail -n1

де ми сортуємо файл /etc/passwd в порядку зростання по колонці UID і перенаправляємо результат по каналу у команду tail. Далі виводиться перше значення відсортованого списку.



[23] Деякі приклади в цьому розділі ґрунтуються на реальній роботі з файлами журналів деяких серверів (служб, демонів). Переконайтеся, що у вас запущено syslogd (дозволяє журналювати дії демонів) і відповідний демон (в нашому разі Postfix), і що ви в працюєте під root'ом. Звичайно ж ви завжди можете застосовувати наші приклади до інших файлів.

[24] Наприклад, файл /var/log/mail/info містить інформацію про всі відправлені листи, повідомлення про вибирання пошти користувачами по протоколу POP і т.п.

[25] Хоча можна виконати фільтрацію просто за шаблоном стану, будь ласка, дозвольте нам показати вам цьому прикладі нову команду.

[26] Тут ми тільки коротенько розглянемо sort. Про її можливості можна написати цілу книжку.