Канали та фільтри
Останнє оновлення 2025-09-04 | Редагувати цю сторінку
Приблизний час: 35 хвилин
Огляд
Питання
- Як я можу комбінувати команди, що вже існують, щоб робити нові речі?
- Як відобразити лише частину виведених даних?
Цілі
- Зрозуміти перевагу поєднання команд за допомогою каналів та фільтрів.
- Навчитись комбінувати послідовності команд для отримання нового результату
- Навчитись перенаправляти вивід команди до файлу.
- Зрозуміти, що зазвичай відбувається, якщо програмі або конвеєру не надається жодних вхідних даних для обробки.
Тепер, після ознайомлення з основними командами, ми можемо нарешті
розглянути найпотужнішу функцію терміналу: здатність комбінувати наявні
програми різними способами. Ми почнемо з каталогу
shell-lesson-data/exercise-data/proteins
, який містить
шість файлів, що описують деякі прості органічні молекули. Розширення
.pdb
вказує на те, що ці файли мають формат Protein Data
Bank - простий текстовий формат, який визначає тип і положення кожного
атома в молекулі.
ВИХІД
cubane.pdb methane.pdb pentane.pdb
ethane.pdb octane.pdb propane.pdb
Запустимо наприклад цю команду:
ВИХІД
20 156 1158 cubane.pdb
wc
- команда для підрахунку слів (англ. ‘word count’):
вона рахує кількість рядків, слів і символів у файлах (повертаючи
значення в такому порядку зліва направо).
Якщо ми виконаємо команду wc *.pdb
, то символ
*
у *.pdb
відповідає будь-якій кількості
символів (включаючи пустий рядок), тож термінал перетворить
*.pdb
на перелік усіх файлів з розширенням
.pdb
у поточному каталозі:
ВИХІД
20 156 1158 cubane.pdb
12 84 622 ethane.pdb
9 57 422 methane.pdb
30 246 1828 octane.pdb
21 165 1226 pentane.pdb
15 111 825 propane.pdb
107 819 6081 total
Зверніть увагу, що wc *.pdb
в останньому рядку свого
виводу також показує загальну кількість усіх рядків у перелічених
файлах.
Якщо ми виконаємо wc -l
замість просто wc
,
то виводитиметься лише кількість рядків у файлах:
ВИХІД
20 cubane.pdb
12 ethane.pdb
9 methane.pdb
30 octane.pdb
21 pentane.pdb
15 propane.pdb
107 total
Параметри -m
та -w
з командою
wc
дозволяють показувати тільки кількість символів або
тільки кількість слів у файлах.
Чому нічого не відбувається?
Що станеться, коли команді, яка має обробляти файл, не надати його назву? Наприклад, що буде, якщо ми наберемо:
але не будемо вводити *.pdb
(або щось інше) після цієї
команди? Оскільки команда не отримала жодних назв файлів,
wc
вважає, що треба обробляти введені дані з командного
рядка, тому вона просто очікує, поки ми надамо їй якісь дані
інтерактивно. Ззовні, однак, це виглядає так, ніби команда нічого не
робить.
Якщо ви припустилися такої помилки, ви можете вийти з цього стану, утримуючи клавішу control (Ctrl), та один раз натиснувши клавішу C: Ctrl+C. Потім відпустіть обидві клавіші.
Перехоплення виводу з команд
Який з цих файлів містить найменшу кількість рядків? Це легко визначити, коли файлів лише шість, але що робити, якщо їх 6000? Наш перший крок до пошуку рішення - це запуск наступної команди:
Символ ‘більше ніж’, тобто >
, вказує терміналу
перенаправити вивід команди до файлу замість виведення
його на екран. Ця команда не виводить дані на екран, оскільки увесь
вивід wc
записується до файлу lengths.txt
.
Якщо файлу не існувало до виконання команди, його буде створено. Якщо
файл вже існує, він буде непомітно перезаписаний, що може призвести до
втрати даних. Таким чином, перенаправлення команд
вимагає обережності.
Команда ls lengths.txt
підтверджує, що файл існує:
ВИХІД
lengths.txt
Тепер ми можемо вивести вміст файлу lengths.txt
на екран
за допомогою команди cat lengths.txt
. Назва команди
cat
походить від слова ‘concatenate’, тобто об’єднувати, і
вона виводить вміст файлів один за одним. У цьому випадку є лише один
файл, тому cat
просто виводить нам його вміст:
ВИХІД
20 cubane.pdb
12 ethane.pdb
9 methane.pdb
30 octane.pdb
21 pentane.pdb
15 propane.pdb
107 total
Виведення сторінки за сторінкою
У цьому уроці, для зручності та послідовності ми й надалі
використовуватимемо команду cat
, але її недолік полягає в
тому, що вона завжди показує весь файл одразу. Більш корисною на
практиці є команда less
(наприклад,
less lengths.txt
). Вона виводить стільки вмісту файлу,
скільки вміщується в одному екрані, а потім робить паузу. Ви можете
перейти на один екран вперед, натиснувши пробіл, або на один екран
назад, натиснувши клавішу b
. Щоб вийти з перегляду вмісту
файлу, натисніть q
.
Фільтрування виводу
Далі ми скористаємося командою sort
для сортування
вмісту файлу lengths.txt
. Але спершу виконаємо вправу, щоб
трохи ознайомитися з командою sort
:
Що робить sort -n
?
Файл shell-lesson-data/exercise-data/numbers.txt
містить
наступні рядки:
10
2
19
22
6
Якщо ми виконаємо команду sort
для цього файлу, то
отримаємо наступне:
ВИХІД
10
19
2
22
6
Якщо ми виконаємо команду sort -n
для того ж файлу, то
замість цього ми отримаємо наступне:
ВИХІД
2
6
10
19
22
Поясніть, чому -n
має такий ефект.
Опція -n
задає числове, а не алфавітно-цифрове
сортування.
Ми також використовуватимемо опцію -n
, щоб задати
числове сортування замість алфавітно-цифрового. Це не змінить
файл; натомість відсортований результат буде виведено на екран:
ВИХІД
9 methane.pdb
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb
107 total
Ми можемо записати відсортований список рядків в інший тимчасовий
файл з назвою sorted-lengths.txt
, додавши
> sorted-lengths.txt
після команди, так само як ми
використовували > lengths.txt
, щоб записати вивід
wc
у lengths.txt
. Потім можна скористатися
командою head
, щоб отримати перші кілька рядків у
sorted-lengths.txt
:
ВИХІД
9 methane.pdb
Використання -n 1
з head
вказує команді, що
нам потрібен лише перший рядок файлу; -n 20
поверне перші
20 тощо. Оскільки файл sorted-lengths.txt
містить довжини
наших файлів, впорядковані від найменшої до найбільшої, виведенням
head
має бути файл з найменшою кількістю рядків.
Що означає >>
?
Ми вже розглядали оператор >
, але ще існує схожий
оператор >>
, який працює трохи інакше. Ми дізнаємося
про відмінності між цими двома операторами, надрукувавши кілька рядків.
Для виведення рядків ми можемо скористатися командою echo
,
наприклад:
ВИХІД
The echo command prints text
Тепер протестуйте наведені нижче команди, щоб виявити різницю між цими двома операторами:
та:
Підказка: Спробуйте виконати кожну команду двічі поспіль, а потім переглянути вихідні файли.
У першому прикладі з >
рядок ‘hello’ записується до
файлу testfile01.txt
, але файл перезаписується кожного
разу, коли ми запускаємо команду.
З другого прикладу ми бачимо, що оператор >>
також
записує рядок ‘hello’ у файл (у цьому випадку
testfile02.txt
), але додає рядок до файлу, якщо останній
вже існує (тобто, коли ми запускаємо його вдруге).
Додавання даних у кінець файлу
Ми вже знайомі з командою head
, яка виводить рядки з
початку файлу. Команда tail
схожа на неї, але виводить
рядки з кінця файлу.
Розглянемо файл
shell-lesson-data/exercise-data/animal-counts/animals.csv
.
Після виконання цих команд оберіть відповідь, яка відповідає вмісту
файлу animals-subset.csv
:
- Перші три рядки файлу
animals.csv
- Останні два рядки файлу
animals.csv
- Перші три рядки та останні два рядки файлу
animals.csv
- Другий і третій рядки файлу
animals.csv
Варіант 3 є правильним. Щоб варіант 1 був правильним, потрібно
виконати лише команду head
. Щоб варіант 2 був правильним,
нам слід виконати лише команду tail
. Щоб варіант 4 був
коректним, нам слід передати вивід команди head
у команду
tail -n 2
виконавши
head -n 3 animals.csv | tail -n 2 > animals-subset.csv
Передача виводу іншій команді
У нашому прикладі для пошуку файлу з найменшою кількістю рядків, ми
використовуємо два проміжні файли lengths.txt
та
sorted-lengths.txt
для зберігання результатів. Такий підхід
може збивати з пантелику, оскільки навіть зрозумівши як працюють
wc
, sort
і head
, ці проміжні
файли ускладнюють відстеження всього процесу. Щоб легше було зрозуміти,
можна одночасно виконати sort
і head
:
ВИХІД
9 methane.pdb
Вертикальна риска |
між двома командами називається
каналом (pipe). Вона вказує терміналу, що вивід команди
ліворуч слід використати як вхідні дані для команди праворуч.
Це усуває необхідність у файлі sorted-lengths.txt
.
Поєднання декількох команд
Ніщо не заважає нам з’єднувати канали послідовно. Наприклад, ми
можемо надсилати вивід wc
безпосередньо до
sort
, а потім результат — до head
. Це усуває
необхідність у будь-яких проміжних файлах.
Ми почнемо з використання каналу для надсилання виводу
wc
до sort
:
ВИХІД
9 methane.pdb
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb
107 total
Потім ми можемо передати цей вивід через інший канал до
head
, отже повний конвеєр буде мати наступний вигляд:
ВИХІД
9 methane.pdb
Це подібне тому, як в математиці ми розглядаємо складні функції на
кшталт log(3x) і кажемо ‘логарифм трьох x*’. У нашому випадку,
обчислюється ‘head від sort від підрахунку кількості рядків у файлах
*.pdb
’.
Перенаправлення та канали, використані в останніх кількох командах, проілюстровані нижче:
З’єднання команд у конвеєр
У нашому поточному каталозі ми хочемо знайти 3 файли, які мають найменшу кількість рядків. Яка з наведених нижче команд підійде для цього?
wc -l * > sort -n > head -n 3
wc -l * | sort -n | head -n 1-3
wc -l * | head -n 3 | sort -n
wc -l * | sort -n | head -n 3
Варіант 4 є рішенням. Символ каналу |
використовується
для під’єднання виводу однієї команди до входу іншої. Символ
>
використовується для перенаправлення стандартного
виводу до файлу. Спробуйте це у каталозі
shell-lesson-data/exercise-data/proteins
!
Інструменти, створені для співробітництва
Представлена вище можливість комбінування програм є причиною успіху
Unix. Замість створення величезних програми, які намагаються робити
багато різних речей, розробники Unix зосередилися на створенні численних
простих інструментів, кожен з яких добре виконує одну роботу і при цьому
чудово взаємодіє з іншими. Ця модель програмування називається ‘канали
та фільтри’. Ми вже бачили приклад каналів; а
фільтри — це програми на кшталт wc
або
sort
, які перетворюють потік вхідних даних у потік
вихідних. Майже всі стандартні інструменти Unix можуть працювати таким
чином. Якщо їм не вказано робити інше, такі програми читають дані зі
стандартного вводу, виконують з ними певні дії та записують результат у
стандартний вивід.
Головне полягає в тому, що будь-яка програма, яка зчитує рядки тексту зі стандартного вводу і записує їх у стандартний вивід, може бути об’єднана з будь-якою іншою програмою, яка працює так само. Ви можете і повинні писати свої програми таким чином, щоб ви та інші люди могли з’єднувати їх через канали і тим самим суттєво збільшуючи їхню потужність.
Розуміння роботи з каналами
Файл з назвою animals.csv
(у каталозі
shell-lesson-data/exercise-data/animal-counts
) містить
наступні дані:
2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-06,fox,4
2012-11-07,rabbit,16
2012-11-07,bear,1
Який текст проходить через кожен із каналів та фінальне
перенаправлення у конвеєрі нижче? Зауважте, що команда
sort -r
сортує у зворотному порядку.
Підказка: створюйте конвеєр по одній команді за раз, щоб перевіряти своє розуміння
Команда head
виділяє перші 5 рядків з файлу
animals.csv
. Потім останні 3 рядки виділяються з попередніх
5 за допомогою команди tail
. За допомогою команди
sort -r
ці 3 рядки сортуються у зворотному порядку. І
нарешті, результат перенаправляється до файлу final.txt
.
Вміст цього файлу можна перевірити, виконавши команду
cat final.txt
. Файл повинен містити наступні рядки:
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-05,raccoon,7
Конструювання каналу
Для файлу animals.csv
з попередньої вправи розглянемо
наступну команду:
Команда cut
використовується для видалення або
‘вирізання’ певних частин кожного рядка у файлі. Вона очікує, що рядки
буде розділено на стовпчики символом Tab. Символ, який
використовується таким чином, називається роздільником.
У наведеному вище прикладі ми використали опцію -d
, щоб
вказати кому як роздільник. Ми також використали опцію -f
,
щоб зазначити, що ми хочемо вилучити друге поле (стовпчик). Це призведе
до наступного результату:
ВИХІД
deer
rabbit
raccoon
rabbit
deer
fox
rabbit
bear
Команда uniq
відфільтровує сусідні однакові рядки у
файлі. Як можна розширити цей конвеєр (за допомогою uniq
та
інших команд), щоб з’ясувати, назви яких тварин містяться у файлі (без
повторень у їхніх назвах)?
Який з каналів використати?
Файл animals.csv
містить 8 рядків даних, відформатованих
наступним чином:
ВИХІД
2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
...
Команда uniq
має опцію -c
, яка підраховує
кількість разів, коли рядок зʼявляється у вхідних даних. Припускаючи що
ваш поточний каталог має назву
shell-lesson-data/exercise-data/animal-counts
, яку команду
слід використати, щоб створити таблицю у файлі з підрахунком загальної
кількості тварин кожного типу?
sort animals.csv | uniq -c
sort -t, -k2,2 animals.csv | uniq -c
cut -d, -f 2 animals.csv | uniq -c
cut -d, -f 2 animals.csv | sort | uniq -c
cut -d, -f 2 animals.csv | sort | uniq -c | wc -l
Варіант 4. Це правильна відповідь. Якщо вам важко зрозуміти, чому,
спробуйте виконати команди або фрагменти конвеєру (перед цим
переконайтеся, що ви перебуваєте у каталозі
shell-lesson-data/exercise-data/animal-counts
).
Конвеєр Неллі: перевірка файлів
Неллі обробила свої зразки в аналізаторах і створила 17 файлів в
каталозі north-pacific-gyre
, описаному раніше. Для швидкої
перевірки, вона переходить у каталог shell-lesson-data
та
набирає:
На виході вона отримує 18 рядків, які виглядають наступним чином:
ВИХІД
300 NENE01729A.txt
300 NENE01729B.txt
300 NENE01736A.txt
300 NENE01751A.txt
300 NENE01751B.txt
300 NENE01812A.txt
... ...
Тепер вона набирає наступне:
ВИХІД
240 NENE02018B.txt
300 NENE01729A.txt
300 NENE01729B.txt
300 NENE01736A.txt
300 NENE01751A.txt
Ого - несподіванка! Один з файлів на 60 рядків коротший за інші. Коли вона повертається до цього файлу та перевіряє його, вона бачить, що зробила цей аналіз о 8:00 ранку в понеділок — хтось, можливо, користувався машиною на вихідних, і вона забула її перезавантажити. Перед тим, як повторно проаналізувати цей зразок, вона перевіряє, чи є файли, що містять забагато даних:
ВИХІД
300 NENE02040B.txt
300 NENE02040Z.txt
300 NENE02043A.txt
300 NENE02043B.txt
5040 total
Ці цифри мають сенс — але що робить ця ‘Z’ у другому рядку? Всі її зразки мають бути позначені ‘A’ або ‘B’; за попередньою домовленістю її лабораторія використовує ‘Z’ для позначення зразків з недостатньою інформацією. Щоб знайти інші подібні зразки, вона робить наступне:
ВИХІД
NENE01971Z.txt NENE02040Z.txt
Справді, коли вона перевіряє файл журналу на своєму ноутбуці, то
виявляється, що глибина не була записана для жодного з цих зразків.
Оскільки отримати цю інформацію іншим способом вже неможливо, їй
доведеться виключити ці два файли з аналізу. Вона може видалити їх за
допомогою rm
, але деякі подальші аналізи даних не
вимагатимуть інформації про глибину, тому їй буде потрібно обережно
обирати файли за допомогою шаблонів
NENE*A.txt NENE*B.txt
.
Видалення непотрібних файлів
Припустимо, ви хочете видалити файли з обробленими даними й зберегти
лише вихідні файли та скрипт обробки для економії місця у сховищі.
Вихідні файли закінчуються на .dat
, а оброблені файли
закінчуються на .txt
. Яка з наведених нижче команд видалить
усі оброблені файли даних і тільки їх?
rm ?.txt
rm *.txt
rm * .txt
rm *.*
Це призведе до вилучення файлів
.txt
з односимвольними назвамиЦе правильна відповідь
Термінал розширить шаблон
*
до переліку усіх файлів у поточному каталозі, таким чином, команда спробує видалити всі знайдені файли та додатковий файл з назвою `.txt’Термінал розширює
*.*
до переліку усіх файлів, назви яких містять принаймні одну крапку (.
), включно з обробленими файлами (.txt
), і вихідними файлами (.dat
)
-
wc
підраховує рядки, слова та символи у своїх вхідних даних. -
cat
виводить вміст своїх вхідних даних. -
sort
сортує вхідні дані. -
head
за замовчуванням (тобто без додаткових аргументів) виводить перші 10 рядків вхідних даних. -
tail
за замовчуванням (тобто без додаткових аргументів) виводить останні 10 рядків вхідних даних. -
command > [file]
перенаправляє вивід команди у файл (перезаписуючи будь-який наявний вміст цього файлу, якщо файл вже існує). -
command >> [file]
додає вивід команди до файлу. -
[first] | [second]
є конвеєром: вихід першої команди використовується як вхідні дані для другої. - Найкращий спосіб використання терміналу - це комбінування простих однозадачних програм (фільтрів) за допомогою каналів.