Поиск по сайту:

Что такое 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: стандартный вывод

Реакция на каналы и редиректы

Чтобы облегчить чье-то введение в предмет, распространенным приемом является преподавание упрощенной версии темы. Например, в случае с грамматикой нам говорят, что действует правило «I перед E, кроме как после C». Но на самом деле исключений из этого правила больше, чем случаев, которые ему подчиняются.

Аналогичным образом, когда речь идет о stdin, stdout и stderr, удобно изложить общепринятую аксиому, согласно которой процесс не знает и не заботится о том, где заканчиваются три его стандартных потока. Должен ли процесс заботиться о том, идет ли его вывод на терминал или перенаправляется в файл? Может ли он даже сказать, поступает ли его ввод с клавиатуры или передается ему из другого процесса?

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

Мы можем легко увидеть это изменение в поведении. Попробуйте эти две команды:

ls
ls | cat

Команда ls ведет себя иначе, если ее выходные данные (stdout) передаются другой команде. Это ls, который переключается на вывод в один столбец, а не преобразование, выполняемое cat. И ls делает то же самое, если его вывод перенаправляется:

ls > capture.txt
cat capture.txt

Перенаправление stdout и stderr

Есть преимущество в том, что сообщения об ошибках доставляются выделенным потоком. Это означает, что мы можем перенаправить вывод команды (stdout) в файл и по-прежнему видеть любые сообщения об ошибках (stderr) в окне терминала. При необходимости вы можете реагировать на ошибки по мере их возникновения. Он также не позволяет сообщениям об ошибках загрязнять файл, в который был перенаправлен stdout.

Введите следующий текст в редактор и сохраните его в файл с именем error.sh.

#!/bin/bash

echo "About to try to access a file that doesn't exist"
cat bad-filename.txt

Сделайте скрипт исполняемым с помощью этой команды:

chmod +x error.sh

Первая строка скрипта выводит текст в окно терминала через поток stdout. Вторая строка пытается получить доступ к несуществующему файлу. Это создаст сообщение об ошибке, которое будет доставлено через stderr.

Запустите скрипт с помощью этой команды:

./error.sh

Мы видим, что оба потока вывода, stdout и stderr, отображаются в окнах терминала.

Попробуем перенаправить вывод в файл:

./error.sh > capture.txt

Сообщение об ошибке, доставленное через stderr, по-прежнему отправляется в окно терминала. Мы можем проверить содержимое файла, чтобы увидеть, перешел ли вывод stdout в файл.

cat capture.txt

Вывод из stdin был перенаправлен в файл, как и ожидалось.

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

Чтобы явно перенаправить  stdout, используйте эту инструкцию перенаправления:

1>

Чтобы явно перенаправить  stderr, используйте эту инструкцию перенаправления:

2>

Давайте снова попробуем наш тест, и на этот раз мы будем использовать 2>:

./error.sh 2> capture.txt

Сообщение об ошибке перенаправляется, и в окно терминала отправляется сообщение stdout echo:

Давайте посмотрим, что находится в файле capture.txt.

cat 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: перенаправляет поток stdout в файл 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 (терминал) возвращает значение true (0), если файл, связанный с дескриптором файла, завершается в окне терминала. Мы использовали файловый дескриптор 0 в качестве аргумента теста, который представляет  stdin.

Если stdin подключен к окну терминала, тест будет верным. Если stdin подключен к файлу или каналу, тест завершится ошибкой.

Мы можем использовать любой удобный текстовый файл для создания входных данных для скрипта. Здесь мы используем файл с именем dummy.txt.

./input.sh < dummy.txt

Вывод показывает, что скрипт распознает ввод не с клавиатуры, а из файла. Если вы захотите, вы можете соответствующим образом изменить поведение своего скрипта.

Это было с перенаправлением файлов, попробуем с пайпом.

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 для представления файлового дескриптора для stdout.

Давайте попробуем. Мы будем передавать вывод через cat.

./output | cat

Сценарий распознает, что его вывод не идет прямо в окно терминала.

Мы также можем протестировать скрипт, перенаправив вывод в файл.

./output.sh > capture.txt

Выхода в окно терминала нет, нас молча возвращают в командную строку. Как мы и ожидали.

Мы можем заглянуть внутрь файла capture.txt, чтобы увидеть, что было захвачено. Для этого используйте следующую команду.

cat capture.sh

Опять же, простой тест в нашем скрипте определяет, что поток stdout не отправляется напрямую в окно терминала.

Если мы запустим скрипт без каких-либо каналов или перенаправлений, он должен обнаружить, что stdout доставляется непосредственно в окно терминала.

./output.sh

И это именно то, что мы видим.

Потоки Сознания

Зная, как определить, подключены ли ваши скрипты к окну терминала, каналу или перенаправляются, вы сможете соответствующим образом настроить их поведение.

Вывод журналов и диагностики может быть более или менее подробным, в зависимости от того, идет ли он на экран или в файл. Сообщения об ошибках могут записываться в файл, отличный от обычного вывода программы.

Как обычно бывает, чем больше знаний, тем больше возможностей.

RELATED: Best Linux Laptops for Developers and Enthusiasts