stdin, stdout і stderr — це три потоки даних, які створюються під час запуску команди Linux. Ви можете використовувати їх, щоб визначити, передаються ваші сценарії або переспрямовуються. Ми покажемо вам, як.
Потоки об’єднуються в дві точки
Як тільки ви почнете вивчати Linux і Unix-подібні операційні системи, ви зустрінете терміни stdin, stdout і stederr. Це такі три стандартні потоки які встановлюються під час виконання команди Linux. У обчислювальній техніці потік — це те, що може передавати дані. У разі цих потоків ці дані є текстовими.
Потоки даних, як і водні потоки, мають два кінці. У них є джерело і відтік. Будь-яка команда Linux, яку ви використовуєте, забезпечує один кінець кожного потоку. Інший кінець визначається оболонкою, яка запустила команду. Цей кінець буде підключено до вікна терміналу, підключено до каналу або перенаправлено до файлу чи іншої команди відповідно до командного рядка, який запустив команду.
Стандартні потоки Linux
У Linux stdin є стандартним потоком введення. Це приймає текст як вхід. Виведення тексту з команди в оболонку доставляється через потік stdout (стандартний вихід). Повідомлення про помилки від команди надсилаються через потік stderr (стандартна помилка).
Таким чином, ви можете побачити, що є два вихідні потоки, stdout і stderr, і один вхідний потік, stdin. Оскільки повідомлення про помилки та звичайний вихід мають власний канал для перенесення їх до вікна терміналу, їх можна обробляти незалежно один від одного.
Потоки обробляються як файли
Потоки в Linux — як і майже все інше — розглядаються як файли. Ви можете читати текст з файлу, а можете записувати текст у файл. Обидві ці дії включають потік даних. Отже, концепція обробки потоку даних як файлу не так вже й складна.
Кожному файлу, пов’язаному з процесом, присвоюється унікальний номер для його ідентифікації. Це відомо як дескриптор файлу. Щоразу, коли потрібно виконати дію з файлом, дескриптор файлу використовується для ідентифікації файлу.
Ці значення завжди використовуються для stdin, stdout і stderr:
0: стандартний номер
1: стандартний вихід
2: stderr
Реакція на канали та перенаправлення
Щоб полегшити чиєсь ознайомлення з предметом, поширеною технікою є викладання спрощеної версії теми. Наприклад, з граматикою нам кажуть, що правило: «I перед E, крім C». Але насправді там є більше винятків із цього правила чим є випадки, які підкоряються йому.
Подібним чином, говорячи про stdin, stdout і stderr, зручно висловлювати прийняту аксіому, що процес не знає і не хвилює, де закінчуються три його стандартні потоки. Чи повинен процес піклуватися про те, чи буде його вихід на термінал, чи буде перенаправлений у файл? Чи може він навіть визначити, чи надходить його введення з клавіатури чи передається в нього з іншого процесу?
Насправді процес знає — або принаймні може дізнатися, якщо вирішить перевірити — і він може відповідно змінити свою поведінку, якщо автор програмного забезпечення вирішить додати цю функцію.
Ми дуже легко бачимо цю зміну в поведінці. Спробуйте ці дві команди:
ls
ls | cat
Команда ls поводиться інакше, якщо її вихід (стандартний вихід) передається в іншу команду. Саме ls перемикається на вихід одного стовпця, це не перетворення, яке виконує cat. І ls робить те ж саме, якщо його вихід перенаправляється:
ls > capture.txt
Перенаправлення stdout і stderr
Є перевага в тому, що повідомлення про помилки доставляються за допомогою спеціального потоку. Це означає, що ми можемо перенаправити вихід команди (stdout) у файл і все одно бачити повідомлення про помилки (stderr) у вікні терміналу. Ви можете реагувати на помилки, якщо вам потрібно, у міру їх виникнення. Це також зупиняє повідомлення про помилки від забруднення файлу, до якого було перенаправлено стандартний вихід.
Введіть наступний текст у редактор і збережіть його у файлі під назвою error.sh.
#!/bin/bash echo "About to try to access a file that doesn't exist" cat bad-filename.txt
Зробіть скрипт виконуваним за допомогою цієї команди:
chmod +x error.sh
Перший рядок сценарію передає текст у вікно терміналу через потік стандартного виведення. Другий рядок намагається отримати доступ до файлу, який не існує. Це згенерує повідомлення про помилку, яке буде доставлено через stderr.
Запустіть скрипт за допомогою цієї команди:
./error.sh
Ми бачимо, що обидва потоки виводу, stdout і stderr, відображаються у вікнах терміналу.
Спробуємо перенаправити вихід у файл:
./error.sh > capture.txt
Вихід із stdin був перенаправлений у файл, як і очікувалося.
Символ перенаправлення > за замовчуванням працює зі стандартним виведенням. Ви можете використовувати один із числових дескрипторів файлу, щоб вказати, який стандартний вихідний потік ви хочете перенаправити.
Щоб явно переспрямувати стандартний вихід, скористайтеся цією інструкцією переспрямування:
1>
Щоб явно переспрямувати stderr, скористайтеся цією інструкцією переспрямування:
2>
Давайте знову спробуємо виконати наш тест, і цього разу ми використаємо 2>:
./error.sh 2> capture.txt
Повідомлення stderr знаходиться в capture.txt, як і очікувалося.
Перенаправлення як stdout, так і stderr
Звичайно, якщо ми можемо переспрямувати stdout або stderr до файлу незалежно один від одного, ми повинні мати можливість переспрямувати їх обидва одночасно, до двох різних файлів?
Так, ми можемо. Ця команда спрямовує stdout до файлу під назвою capture.txt, а stderr — до файлу під назвою error.txt.
./error.sh 1> capture.txt 2> error.txt
Давайте перевіримо вміст кожного файлу:
cat capture.txt
cat error.txt
Переспрямування stdout і stderr до того самого файлу
Це акуратно, у нас кожен із стандартних вихідних потоків спрямовується до окремого файлу. Єдина інша комбінація, яку ми можемо зробити, це надіслати як stdout, так і stderr до одного файлу.
Ми можемо досягти цього за допомогою такої команди:
./error.sh > capture.txt 2>&1
Давайте розберемо це.
./error.sh: запускає файл сценарію error.sh.
> capture.txt: перенаправляє потік стандартного виведення до файлу capture.txt. > є скороченням від 1>.
2>&1: для цього використовується інструкція переспрямування &>. Ця інструкція дозволяє вказати оболонці, щоб один потік дістався до того ж місця призначення, що й інший потік. У цьому випадку ми говоримо «переспрямувати потік 2, stderr, до того самого місця призначення, куди переспрямовується потік 1, stdout».
Давайте перевіримо файл capture.txt і подивимося, що в ньому.
cat capture.txt
Потоки stdout і stderr були перенаправлені в один цільовий файл.
Щоб вихід потоку був перенаправлений і безшумно викинутий, спрямуйте вихід на /dev/null.
Виявлення переспрямування всередині сценарію
Ми обговорили, як команда може виявити, чи перенаправляється якийсь із потоків, і може змінити свою поведінку відповідно. Чи можемо ми зробити це за допомогою наших власних сценаріїв? Так, ми можемо. І це дуже проста техніка для розуміння та застосування.
Введіть наступний текст у редактор і збережіть його як input.sh.
#!/bin/bash if [ -t 0 ]; then echo stdin coming from keyboard else echo stdin coming from a pipe or a file fi
Використовуйте таку команду, щоб зробити його виконуваним:
chmod +x input.sh
Розумна частина – це тест у квадратних дужках. Параметр -t (термінал) повертає істину (0), якщо файл пов’язаний з дескриптором файлу завершується у вікні терміналу. Ми використали дескриптор файлу 0 як аргумент для тесту, який представляє stdin.
Якщо stdin підключено до вікна терміналу, тест підтвердиться. Якщо stdin підключено до файлу або каналу, тест не вийде.
Ми можемо використовувати будь-який зручний текстовий файл для створення вхідних даних до сценарію. Тут ми використовуємо файл під назвою dummy.txt.
./input.shThe output shows that the script recognizes that the input isn’t coming from a keyboard, it is coming from a file. If you chose to, you could vary your script’s behavior accordingly.
That was with a file redirection, let’s try it with a pipe.
cat dummy.txt | ./input.shСценарій розпізнає, що його вхідні дані передаються в нього. Точніше, він знову розпізнає, що потік stdin не підключений до вікна терміналу.
Давайте запустимо скрипт без каналів і перенаправлень.
./input.shПотік stdin підключається до вікна терміналу, і сценарій повідомляє про це відповідно.
Щоб перевірити те ж саме з вихідним потоком, нам потрібен новий скрипт. Введіть наступне в редактор і збережіть його як output.sh.
#!/bin/bash if [ -t 1 ]; then echo stdout is going to the terminal window else echo stdout is being redirected or piped fiВикористовуйте таку команду, щоб зробити його виконуваним:
chmod +x input.shЄдина істотна зміна цього сценарію – тест у квадратних дужках. Ми використовуємо цифру 1 для представлення дескриптора файлу для стандартного виведення.
Давайте спробуємо. Ми передаємо вихід через cat.
./output | catСценарій розпізнає, що його вихід не спрямовується безпосередньо до вікна терміналу.
Ми також можемо протестувати сценарій, перенаправивши вихід у файл.
./output.sh > capture.txtНемає виводу у вікно терміналу, ми мовчки повертаємося до командного рядка. Як ми і очікували.
Ми можемо зазирнути у файл capture.txt, щоб побачити, що було знято. Використовуйте для цього наступну команду.
cat capture.shЗнову ж таки, простий тест у нашому скрипті виявляє, що потік стандартного виведення не надсилається безпосередньо у вікно терміналу.
Якщо ми запустимо сценарій без будь-яких каналів або перенаправлень, він повинен виявити, що stdout доставляється безпосередньо у вікно терміналу.
./output.shІ це саме те, що ми бачимо.
Потоки Свідомості
Знання того, як визначити, чи підключені ваші сценарії до вікна терміналу, каналу, чи перенаправляються, дозволяє відповідно налаштувати їх поведінку.
Реєстрація та вихід діагностики можуть бути більш чи менш детальними, залежно від того, на екрані чи у файл. Повідомлення про помилки можуть бути записані в інший файл, ніж звичайний вихід програми.
Як це зазвичай буває, більше знань приносить більше можливостей.