Канали та фільтри
Огляд
Викладання: 25 хв
Вправи: 10 хвПитання
Як я можу комбінувати команди, що вже існують, щоб робити нові речі?
Цілі
Перенаправити вивід команди до файлу.
Створити конвеєри команд з двома та більше ступенями.
Пояснити, що зазвичай відбувається, якщо програмі або конвеєру не надається жодних вхідних даних для обробки.
Пояснити перевагу поєднання команд з каналами та фільтрами.
Тепер, коли ми знаємо декілька основних команд,
ми можемо нарешті розглянути найпотужнішу можливість терміналу:
легкість, з якою він дозволяє нам комбінувати програми, що існують, новими способами.
Ми почнемо з каталогу shell-lesson-data/exercise-data/proteins
,
який містить шість файлів, що описують деякі прості органічні молекули.
Розширення .pdb
вказує на те, що ці файли у форматі Protein Data Bank,
простому текстовому форматі, який визначає тип і положення кожного атома в молекулі.
$ ls
cubane.pdb methane.pdb pentane.pdb
ethane.pdb octane.pdb propane.pdb
Запустимо приклад команди:
$ wc cubane.pdb
20 156 1158 cubane.pdb
wc
- команда ‘порахуй слова’ (англ. ‘word count’):
вона підраховує кількість рядків, слів і символів у файлах (зліва направо, у такому порядку).
Якщо ми виконаємо команду wc *.pdb
, то *
у *.pdb
збігається з нулем або більшою кількістю символів,
тож термінал перетворить *.pdb
на перелік усіх .pdb
-файлів у поточному каталозі:
$ 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
,
у виводі буде показано лише кількість рядків у файлі:
$ 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
, щоб показати
тільки кількість символів або тільки кількість слів у файлах.
Чому воно нічого не робить?
Що станеться, якщо команда має обробити файл, але ми не вкажемо імені файлу? Наприклад, що буде, якщо ми наберемо:
$ wc -l
але не будемо вводити
*.pdb
(або щось інше) після команди? Оскільки команда не має жодних назв файлів,wc
вважає, що вона повинна обробляти дані, введені у командному рядку, тож вона просто сидить і чекає, поки ми надамо їй інтерактивно якісь дані. Ззовні, однак, все, що ми бачимо, це те, що вона, здається, нічого не робить.Якщо ви припустилися такої помилки, ви можете вийти з цього стану, утримуючи клавішу control (Ctrl), один раз натиснувши клавішу C, відпустивши після цього клавішу Ctrl. Ctrl+C
Перехоплення виводу з команд
Який з цих файлів містить найменше рядків? На це питання легко відповісти, коли файлів всього шість, але що, якщо їх 6000? Наш перший крок до пошуку рішеня - це запуск команди:
$ wc -l *.pdb > lengths.txt
Символ ‘більше ніж’, >
, вказує терміналу перенаправити вивід команди
до файла замість виведення його на екран. (Саме тому виведення на екран не відбувається:
все, що мала вивести команда wc
, було збережено у файлі
lengths.txt
замість цього). Термінал створить
файл, якщо його не існує. Якщо файл існує, його буде
буде перезаписано без будь-яких повідомлень, що може призвести до втрати даних, а отже, вимагає
певної обережності.
Файл ls lengths.txt
підтверджує, що файл існує:
$ ls lengths.txt
lengths.txt
Тепер ми можемо вивести вміст файлу lengths.txt
на екран за допомогою команди cat lengths.txt
.
Команда cat
отримала свою назву від слова ‘concatenate’, тобто об’єднувати,
і вона виводить вміст файлів один за одним.
У цьому випадку є лише один файл,
тому cat
просто відображає нам його вміст:
$ 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
, щоб вказати, що сортування буде
числовим, а не алфавітно-цифровим.
Це не змінить файл;
натомість відсортований результат буде виведено на екран:
$ 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
:
$ 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
має бути файл з найменшою кількістю рядків.
Перенаправлення у той самий файл
Це дуже погана ідея - намагатися перенаправити вихідні дані команди, яка оперує з файлом, у той самий файл. Наприклад:
$ sort -n lengths.txt > lengths.txt
Виконання таких дій може надати вам некоректні результати та/або видалити вміст файлу
lengths.txt
.
Що означає
>>
?Ми бачили використання оператору
>
, але існує схожий оператор>>
, який працює дещо по-іншому. Ми дізнаємося про відмінності між цими двома операторами, надрукувавши кілька рядків. Ми можемо скористатися командоюecho
для виведення рядків, наприклад$ echo Команда echo виводить текст
Команда echo виводить текст
Тепер протестуйте наведені нижче команди, щоб виявити різницю між цими двома операторами:
$ echo hello > testfile01.txt
та:
$ echo hello >> testfile02.txt
Підказка: Спробуйте виконати кожну команду двічі поспіль, а потім переглянути вихідні файли.
Розв’язання
У першому прикладі з
>
рядок ‘hello’ записується до файлуtestfile01.txt
, але файл перезаписується кожного разу, коли ми запускаємо команду.З другого прикладу ми бачимо, що оператор
>>
також записує рядок ‘hello’ у файл (у цьому випадкуtestfile02.txt
), але додає рядок до файлу, якщо останній вже існує (тобто, коли ми запускаємо його вдруге).
Додавання даних
Ми вже зустрічалися з командою
head
, яка друкує рядки з початку файлу. Командаtail
схожа на неї, але друкує рядки з кінця файлу.Розглянемо файл
shell-lesson-data/exercise-data/animal-counts/animals.csv
. Після виконання цих команд виберіть відповідь, яка відповідає файлуanimals-subset.csv
:$ head -n 3 animals.csv > animals-subset.csv $ tail -n 2 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
разом:
$ sort -n lengths.txt | head -n 1
9 methane.pdb
Вертикальна риска |
між двома командами називається каналом.
Він вказує терміналу, що ми хочемо використовувати
вивід команди ліворуч
як вхідні дані для команди праворуч.
Це усунуло необхідність у файлі sorted-lengths.txt
.
Поєднання декількох команд
Ніщо не заважає нам з’єднувати канали послідовно.
Ми можемо, наприклад, надсилати вивід wc
безпосередньо до sort
,
а потім отриманий результат - до head
.
Це усуває необхідність у будь-яких проміжних файлах.
Ми почнемо з використання каналу для відправки виводу wc
до sort
:
$ 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
, отже конвеєр стає наступним:
$ wc -l *.pdb | sort -n | head -n 1
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
сортує у зворотному порядку.$ 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,олень,2 2012-11-05,єнот,7
Конструювання каналу
Для файлу
animals.csv
з попередньої завдання розглянемо наступну команду:>$ cut -d , -f 2 animals.csv
Команда
cut
використовується для видалення або ‘вирізання’ певних частин кожного рядка у файлі, іcut
очікує, що рядки буде розділено на стовпчики символом Tab. Символ, який використовується таким чином, називається відокремлювальним символом. У наведеному вище прикладі ми використовуємо опцію-d
, щоб вказати кому як відокремлювальний символ. Ми також використали опцію-f
, щоб вказати, що ми хочемо вилучити друге поле (стовпчик). Це призведе до наступного результату:deer rabbit raccoon rabbit deer fox rabbit bear
Команда
uniq
відфільтровує сусідні однакові рядки у файлі. Як можна розширити цей конвеєр (за допомогоюuniq
та іншої команди), щоб з’ясувати, які тварини містяться у файлі (без повторень у їхніх назвах)?Розв’язання
$ 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
, яку команду слід використати, щоб створити таблицю, яка показує загальну кількість тварин кожного типу у файлі?
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
, Неллі набирає:
$ 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
... ...
Тепер вона набирає наступне:
$ 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 ранку в понеділок — хтось, можливо, користувався машиною на вихідних, і вона забула її перезавантажити. Перед тим, як повторно проаналізувати цей зразок, вона перевіряє, чи є файли, що містять забагато даних:
$ 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’ для позначення зразків з недостатньою інформацією. Щоб знайти подібні зразки, вона робить наступне:
$ ls *Z.txt
NENE01971Z.txt NENE02040Z.txt
Звісно,
коли вона перевіряє журнал на своєму ноутбуці,
глибина не записана для жодного з цих зразків.
Оскільки вже занадто пізно отримати інформацію в інший спосіб,
вона повинна виключити ці два файли з аналізу.
Вона може видалити їх за допомогою rm
,
але насправді є деякі аналізи, які вона може зробити пізніше, де глибина не має значення,
тож натомість, пізніше їй доведеться бути обережною і вибирати файли за допомогою підстановочних виразів
NENE*A.txt NENE*B.txt
.
Видалення непотрібних файлів
Припустимо, ви хочете видалити файли оброблених даних і зберегти лише сирі файли і скрипт обробки для економії місця у сховищі. Вихідні файли закінчуються на
.dat
, а оброблені файли закінчуються на.txt
. Яка з наведених нижче команд видалить усі оброблені файли даних, і лише оброблені файли даних?
rm ?.txt
rm *.txt
rm * .txt
rm *.*
Розв’язання
- Це призведе до вилучення файлів
.txt
з односимвольними назвами- Це правильна відповідь
- Термінал розширить підстановочний символ
*
так, щоб він відповідав усім файлам у поточному каталозі, таким чином, команда спробує видалити всі знайдені файли і додатковий файл з назвою `.txt’.- Термінал розширить
*.*
, щоб знайти всі файли з будь-яким розширенням, таким чином, ця команда видалить усі файли.
[workshop-repo]: [yaml]: http://yaml.org/
Ключові моменти
wc
підраховує рядки, слова та символи у вхідних даних.cat` виводить вміст своїх вхідних даних.
sort
сортує вхідні дані.
head
виводить перші 10 рядків вхідних даних.
tail
виводить останні 10 рядків вхідних даних.
command > [file]
перенаправляє вивід команди у файл (перезаписуючи наявний вміст).
command >> [file]
додає вивід команди до файлу.
[first] | [second]
є конвеєром: вихід першої команди використовується як вхід для другої.Найкращий спосіб використання терміналу - це використання каналів для об’єднання простих одноцільових програм (фільтрів).