Канали та фільтри

Останнє оновлення 2025-09-04 | Редагувати цю сторінку

Приблизний час: 35 хвилин

Огляд

Питання

  • Як я можу комбінувати команди, що вже існують, щоб робити нові речі?
  • Як відобразити лише частину виведених даних?

Цілі

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

Тепер, після ознайомлення з основними командами, ми можемо нарешті розглянути найпотужнішу функцію терміналу: здатність комбінувати наявні програми різними способами. Ми почнемо з каталогу shell-lesson-data/exercise-data/proteins, який містить шість файлів, що описують деякі прості органічні молекули. Розширення .pdb вказує на те, що ці файли мають формат Protein Data Bank - простий текстовий формат, який визначає тип і положення кожного атома в молекулі.

BASH

$ ls

ВИХІД

cubane.pdb    methane.pdb    pentane.pdb
ethane.pdb    octane.pdb     propane.pdb

Запустимо наприклад цю команду:

BASH

$ wc cubane.pdb

ВИХІД

20 156 1158 cubane.pdb

wc - команда для підрахунку слів (англ. ‘word count’): вона рахує кількість рядків, слів і символів у файлах (повертаючи значення в такому порядку зліва направо).

Якщо ми виконаємо команду wc *.pdb, то символ * у *.pdb відповідає будь-якій кількості символів (включаючи пустий рядок), тож термінал перетворить *.pdb на перелік усіх файлів з розширенням .pdb у поточному каталозі:

BASH

$ wc *.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, то виводитиметься лише кількість рядків у файлах:

BASH

$ wc -l .pdb

ВИХІД

  20  cubane.pdb
  12  ethane.pdb
   9  methane.pdb
  30  octane.pdb
  21  pentane.pdb
  15  propane.pdb
 107  total

Параметри -m та -w з командою wc дозволяють показувати тільки кількість символів або тільки кількість слів у файлах.

Виноска

Чому нічого не відбувається?

Що станеться, коли команді, яка має обробляти файл, не надати його назву? Наприклад, що буде, якщо ми наберемо:

BASH

$ wc -l

але не будемо вводити *.pdb (або щось інше) після цієї команди? Оскільки команда не отримала жодних назв файлів, wc вважає, що треба обробляти введені дані з командного рядка, тому вона просто очікує, поки ми надамо їй якісь дані інтерактивно. Ззовні, однак, це виглядає так, ніби команда нічого не робить.

Якщо ви припустилися такої помилки, ви можете вийти з цього стану, утримуючи клавішу control (Ctrl), та один раз натиснувши клавішу C: Ctrl+C. Потім відпустіть обидві клавіші.

Перехоплення виводу з команд


Який з цих файлів містить найменшу кількість рядків? Це легко визначити, коли файлів лише шість, але що робити, якщо їх 6000? Наш перший крок до пошуку рішення - це запуск наступної команди:

BASH

$ wc -l *.pdb > lengths.txt

Символ ‘більше ніж’, тобто >, вказує терміналу перенаправити вивід команди до файлу замість виведення його на екран. Ця команда не виводить дані на екран, оскільки увесь вивід wc записується до файлу lengths.txt. Якщо файлу не існувало до виконання команди, його буде створено. Якщо файл вже існує, він буде непомітно перезаписаний, що може призвести до втрати даних. Таким чином, перенаправлення команд вимагає обережності.

Команда ls lengths.txt підтверджує, що файл існує:

BASH

$ ls lengths.txt

ВИХІД

lengths.txt

Тепер ми можемо вивести вміст файлу lengths.txt на екран за допомогою команди cat lengths.txt. Назва команди cat походить від слова ‘concatenate’, тобто об’єднувати, і вона виводить вміст файлів один за одним. У цьому випадку є лише один файл, тому cat просто виводить нам його вміст:

BASH

$ cat lengths.txt

ВИХІД

  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, щоб задати числове сортування замість алфавітно-цифрового. Це не змінить файл; натомість відсортований результат буде виведено на екран:

BASH

$ sort -n lengths.txt

ВИХІД

  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:

BASH

$ sort -n lengths.txt > sorted-lengths.txt
$ head -n 1 sorted-lengths.txt

ВИХІД

  9 methane.pdb

Використання -n 1 з head вказує команді, що нам потрібен лише перший рядок файлу; -n 20 поверне перші 20 тощо. Оскільки файл sorted-lengths.txt містить довжини наших файлів, впорядковані від найменшої до найбільшої, виведенням head має бути файл з найменшою кількістю рядків.

Виноска

Перенаправлення до того ж самого файлу

Намагатися перенаправити вивід команди, яка працює з файлом, у той самий файл — дуже погана ідея. Наприклад:

BASH

$ sort -n lengths.txt > lengths.txt

Виконання таких дій може надати вам некоректні результати та/або видалити вміст файлу lengths.txt.

Вправа

Що означає >>?

Ми вже розглядали оператор >, але ще існує схожий оператор >>, який працює трохи інакше. Ми дізнаємося про відмінності між цими двома операторами, надрукувавши кілька рядків. Для виведення рядків ми можемо скористатися командою echo, наприклад:

BASH

$ echo The echo command prints text

ВИХІД

The echo command prints text

Тепер протестуйте наведені нижче команди, щоб виявити різницю між цими двома операторами:

BASH

$ echo hello > testfile01.txt

та:

BASH

$ echo hello >> testfile02.txt

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

У першому прикладі з > рядок ‘hello’ записується до файлу testfile01.txt, але файл перезаписується кожного разу, коли ми запускаємо команду.

З другого прикладу ми бачимо, що оператор >> також записує рядок ‘hello’ у файл (у цьому випадку testfile02.txt), але додає рядок до файлу, якщо останній вже існує (тобто, коли ми запускаємо його вдруге).

Вправа

Додавання даних у кінець файлу

Ми вже знайомі з командою head, яка виводить рядки з початку файлу. Команда tail схожа на неї, але виводить рядки з кінця файлу.

Розглянемо файл shell-lesson-data/exercise-data/animal-counts/animals.csv. Після виконання цих команд оберіть відповідь, яка відповідає вмісту файлу animals-subset.csv:

BASH

$ head -n 3 animals.csv > animals-subset.csv
$ tail -n 2 animals.csv >> animals-subset.csv
  1. Перші три рядки файлу animals.csv
  2. Останні два рядки файлу animals.csv
  3. Перші три рядки та останні два рядки файлу animals.csv
  4. Другий і третій рядки файлу 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:

BASH

$ sort -n lengths.txt | head -n 1

ВИХІД

  9 methane.pdb

Вертикальна риска | між двома командами називається каналом (pipe). Вона вказує терміналу, що вивід команди ліворуч слід використати як вхідні дані для команди праворуч.

Це усуває необхідність у файлі sorted-lengths.txt.

Поєднання декількох команд


Ніщо не заважає нам з’єднувати канали послідовно. Наприклад, ми можемо надсилати вивід wc безпосередньо до sort, а потім результат — до head. Це усуває необхідність у будь-яких проміжних файлах.

Ми почнемо з використання каналу для надсилання виводу wc до sort:

BASH

$ wc -l *.pdb | sort -n

ВИХІД

   9 methane.pdb
  12 ethane.pdb
  15 propane.pdb
  20 cubane.pdb
  21 pentane.pdb
  30 octane.pdb
 107 total

Потім ми можемо передати цей вивід через інший канал до head, отже повний конвеєр буде мати наступний вигляд:

BASH

$ wc -l *.pdb | sort -n | head -n 1

ВИХІД

   9 methane.pdb

Це подібне тому, як в математиці ми розглядаємо складні функції на кшталт log(3x) і кажемо ‘логарифм трьох x*’. У нашому випадку, обчислюється ‘head від sort від підрахунку кількості рядків у файлах *.pdb’.

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

Перенаправлення та канали різних команд: "wc -l *.pdb" перенаправить виведення до терміналу. "wc -l *.pdb lengths" спрямує вивід до файлу "lengths". "wc -l *.pdb | sort -n | head -n 1" побудує конвеєр, де вихід команди "wc" передається як вхідні дані до "sort", вихід команди "sort" є входом для команди "head", а результат команди "head" буде спрямовано до терміналу
Вправа

З’єднання команд у конвеєр

У нашому поточному каталозі ми хочемо знайти 3 файли, які мають найменшу кількість рядків. Яка з наведених нижче команд підійде для цього?

  1. wc -l * > sort -n > head -n 3
  2. wc -l * | sort -n | head -n 1-3
  3. wc -l * | head -n 3 | sort -n
  4. 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 сортує у зворотному порядку.

BASH

$ cat animals.csv | head -n 5 | tail -n 3 | sort -r > final.txt

Підказка: створюйте конвеєр по одній команді за раз, щоб перевіряти своє розуміння

Команда 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 з попередньої вправи розглянемо наступну команду:

BASH

$ cut -d , -f 2 animals.csv

Команда cut використовується для видалення або ‘вирізання’ певних частин кожного рядка у файлі. Вона очікує, що рядки буде розділено на стовпчики символом Tab. Символ, який використовується таким чином, називається роздільником. У наведеному вище прикладі ми використали опцію -d, щоб вказати кому як роздільник. Ми також використали опцію -f, щоб зазначити, що ми хочемо вилучити друге поле (стовпчик). Це призведе до наступного результату:

ВИХІД

deer
rabbit
raccoon
rabbit
deer
fox
rabbit
bear

Команда uniq відфільтровує сусідні однакові рядки у файлі. Як можна розширити цей конвеєр (за допомогою uniq та інших команд), щоб з’ясувати, назви яких тварин містяться у файлі (без повторень у їхніх назвах)?

BASH

$ cut -d , -f 2 animals.csv | sort | 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, яку команду слід використати, щоб створити таблицю у файлі з підрахунком загальної кількості тварин кожного типу?

  1. sort animals.csv | uniq -c
  2. sort -t, -k2,2 animals.csv | uniq -c
  3. cut -d, -f 2 animals.csv | uniq -c
  4. cut -d, -f 2 animals.csv | sort | uniq -c
  5. 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 та набирає:

BASH

$ cd north-pacific-gyre
$ wc -l *.txt

На виході вона отримує 18 рядків, які виглядають наступним чином:

ВИХІД

300 NENE01729A.txt
300 NENE01729B.txt
300 NENE01736A.txt
300 NENE01751A.txt
300 NENE01751B.txt
300 NENE01812A.txt
... ...

Тепер вона набирає наступне:

BASH

$ wc -l *.txt | sort -n | head -n 5

ВИХІД

 240 NENE02018B.txt
 300 NENE01729A.txt
 300 NENE01729B.txt
 300 NENE01736A.txt
 300 NENE01751A.txt

Ого - несподіванка! Один з файлів на 60 рядків коротший за інші. Коли вона повертається до цього файлу та перевіряє його, вона бачить, що зробила цей аналіз о 8:00 ранку в понеділок — хтось, можливо, користувався машиною на вихідних, і вона забула її перезавантажити. Перед тим, як повторно проаналізувати цей зразок, вона перевіряє, чи є файли, що містять забагато даних:

BASH

$ wc -l *.txt | sort -n | tail -n 5

ВИХІД

 300 NENE02040B.txt
 300 NENE02040Z.txt
 300 NENE02043A.txt
 300 NENE02043B.txt
5040 total

Ці цифри мають сенс — але що робить ця ‘Z’ у другому рядку? Всі її зразки мають бути позначені ‘A’ або ‘B’; за попередньою домовленістю її лабораторія використовує ‘Z’ для позначення зразків з недостатньою інформацією. Щоб знайти інші подібні зразки, вона робить наступне:

BASH

$ ls *Z.txt

ВИХІД

NENE01971Z.txt    NENE02040Z.txt

Справді, коли вона перевіряє файл журналу на своєму ноутбуці, то виявляється, що глибина не була записана для жодного з цих зразків. Оскільки отримати цю інформацію іншим способом вже неможливо, їй доведеться виключити ці два файли з аналізу. Вона може видалити їх за допомогою rm, але деякі подальші аналізи даних не вимагатимуть інформації про глибину, тому їй буде потрібно обережно обирати файли за допомогою шаблонів NENE*A.txt NENE*B.txt.

Вправа

Видалення непотрібних файлів

Припустимо, ви хочете видалити файли з обробленими даними й зберегти лише вихідні файли та скрипт обробки для економії місця у сховищі. Вихідні файли закінчуються на .dat, а оброблені файли закінчуються на .txt. Яка з наведених нижче команд видалить усі оброблені файли даних і тільки їх?

  1. rm ?.txt
  2. rm *.txt
  3. rm * .txt
  4. rm *.*
  1. Це призведе до вилучення файлів .txt з односимвольними назвами

  2. Це правильна відповідь

  3. Термінал розширить шаблон * до переліку усіх файлів у поточному каталозі, таким чином, команда спробує видалити всі знайдені файли та додатковий файл з назвою `.txt’

  4. Термінал розширює *.* до переліку усіх файлів, назви яких містять принаймні одну крапку (.), включно з обробленими файлами (.txt), і вихідними файлами (.dat)

Ключові моменти
  • wc підраховує рядки, слова та символи у своїх вхідних даних.
  • cat виводить вміст своїх вхідних даних.
  • sort сортує вхідні дані.
  • head за замовчуванням (тобто без додаткових аргументів) виводить перші 10 рядків вхідних даних.
  • tail за замовчуванням (тобто без додаткових аргументів) виводить останні 10 рядків вхідних даних.
  • command > [file] перенаправляє вивід команди у файл (перезаписуючи будь-який наявний вміст цього файлу, якщо файл вже існує).
  • command >> [file] додає вивід команди до файлу.
  • [first] | [second] є конвеєром: вихід першої команди використовується як вхідні дані для другої.
  • Найкращий спосіб використання терміналу - це комбінування простих однозадачних програм (фільтрів) за допомогою каналів.