Що таке stdin, stdout і stderr у Linux?

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». Але насправді там є більше винятків із цього правила чим є випадки, які підкоряються йому.

  Як завантажити файли в Linux за допомогою Curl

Подібним чином, говорячи про stdin, stdout і stderr, зручно висловлювати прийняту аксіому, що процес не знає і не хвилює, де закінчуються три його стандартні потоки. Чи повинен процес піклуватися про те, чи буде його вихід на термінал, чи буде перенаправлений у файл? Чи може він навіть визначити, чи надходить його введення з клавіатури чи передається в нього з іншого процесу?

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

Ми дуже легко бачимо цю зміну в поведінці. Спробуйте ці дві команди:

ls

ls | cat

Команда ls поводиться інакше, якщо її вихід (стандартний вихід) передається в іншу команду. Саме ls перемикається на вихід одного стовпця, це не перетворення, яке виконує cat. І ls робить те ж саме, якщо його вихід перенаправляється:

ls > capture.txt

ls > capture.txt у вікні терміналу” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<pre>cat capture.txt</pre>
<p><img loading=

Перенаправлення 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

./error.sh > capture.txt у вікні терміналу” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Повідомлення про помилку, яке надходить через stderr, все ще надсилається у вікно терміналу.  Ми можемо перевірити вміст файлу, щоб побачити, чи перейшов вихід стандартного виводу до файлу.</p>
<pre>cat capture.txt</pre>
<p><img loading=

Вихід із stdin був перенаправлений у файл, як і очікувалося.

Символ перенаправлення > за замовчуванням працює зі стандартним виведенням. Ви можете використовувати один із числових дескрипторів файлу, щоб вказати, який стандартний вихідний потік ви хочете перенаправити.

  Як розмістити сервер Mumble на Linux

Щоб явно переспрямувати стандартний вихід, скористайтеся цією інструкцією переспрямування:

1>

Щоб явно переспрямувати stderr, скористайтеся цією інструкцією переспрямування:

2>

Давайте знову спробуємо виконати наш тест, і цього разу ми використаємо 2>:

./error.sh 2> capture.txt

./error.sh 2> capture.txt у вікні терміналу” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Повідомлення про помилку переспрямовується, а ехо-повідомлення стандартного виведення надсилається у вікно терміналу:</p>
<p ><img class=

Повідомлення stderr знаходиться в capture.txt, як і очікувалося.

Перенаправлення як stdout, так і stderr

Звичайно, якщо ми можемо переспрямувати stdout або stderr до файлу незалежно один від одного, ми повинні мати можливість переспрямувати їх обидва одночасно, до двох різних файлів?

Так, ми можемо. Ця команда спрямовує stdout до файлу під назвою capture.txt, а stderr — до файлу під назвою error.txt.

./error.sh 1> capture.txt 2> error.txt

./error.sh 1> capture.txt 2> error.txt у вікні термінала” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Оскільки обидва потоки виводу – стандартний висновок і стандартна помилка – переспрямовуються до файлів, видимий вихід у вікно терміналу.  Ми повертаємося до командного рядка, ніби нічого не сталося.</p>
<p><img loading=

Давайте перевіримо вміст кожного файлу:

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».

./error.sh > capture.txt 2&>1 у вікні термінала” width=”646″ height=”57″ onload=”pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”  onerror=”this.onerror=null;pagespeed.lazyLoadImages.loadIfVisibleAndMaybeBeacon(this);”></p>
<p>Немає видимого результату.  Це обнадійливо.</p>
<p><img loading=

Давайте перевіримо файл 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.

  Як розмістити FTP-сервер на Linux

Якщо stdin підключено до вікна терміналу, тест підтвердиться. Якщо stdin підключено до файлу або каналу, тест не вийде.

Ми можемо використовувати будь-який зручний текстовий файл для створення вхідних даних до сценарію. Тут ми використовуємо файл під назвою dummy.txt.

./input.sh 

The 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

І це саме те, що ми бачимо.

Потоки Свідомості

Знання того, як визначити, чи підключені ваші сценарії до вікна терміналу, каналу, чи перенаправляються, дозволяє відповідно налаштувати їх поведінку.

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

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