Сценарии оболочки — стандартный ввод, вывод и ошибка

Опубликовано: 30 Сентября, 2022

Работая с Linux-приложениями, у нас есть несколько способов получить информацию извне и поместить ее внутрь: аргументы командной строки, переменные среды, файлы. Все эти источники являются законными и хорошими. Но он имеет конечный размер. Другим способом установления связи являются стандартные потоки: входной поток ( stdin ), используемый для получения данных из-за пределов приложения, выходной поток ( stdout ) для помещения данных вне приложения и error для помещения данных вне приложения ( stderr ).

Каждый поток действует как канал: он имеет один и тот же буфер для записи и чтения данных. Этот буфер доступен для чтения из одного приложения и доступен для записи из другого. При чтении размер занятого буфера будет уменьшаться, а при записи — увеличиваться. Если средняя скорость чтения и записи одинакова, то данные, передаваемые по потоку, могут иметь длину любое количество байт.

Потоки ввода/вывода, работающие в примере

Давайте рассмотрим пример приложения «grep», описывающий, как оно с ним взаимодействует.

тело скрипта:

#!/usr/bin/env bash
echo pattern | grep pattern
echo pattern | grep pattern

Скрипт, работающий в консоли:

На этот раз мы запустили 2 команды: «cat stdio1.sh» — для вывода тела скрипта, «./stdio1.sh» — для запуска скрипта. « эхо -шаблон» формирует стандартный поток вывода , который соединяется со стандартным потоком ввода команды grep . Итак, все данные поступают в grep. grep — это приложение, которое записывает из входного потока в выходной поток, если он соответствует шаблону. Первый ввод не соответствует — вывод не ожидается. Вторая строка скрипта «эхо-шаблон | grep pattern» имеет соответствующий ввод — он был передан в вывод grep и отображен в консоли рядом.

Поток ошибок работает в примере

Stderr очень похож на stdout: с одной лишь разницей — он будет содержать отчет об ошибке, если он займет место

тело скрипта:

#!/usr/bin/env bash

ls > /tmp/log
cat /tmp/log

ls /not/existed_path > /tmp/log
cat /tmp/log
cat /tmp/log

rm /tmp/log

Скрипт, работающий в консоли:

В этом сценарии мы делаем список файлов в текущем каталоге («ls») и перенаправляем стандартный вывод в файл («/tmp/log»). Затем мы выводим данные этого файла на консоль из стандартного потока вывода утилиты cat , которая прочитала файл. Затем мы пытаемся перечислить файлы в несуществующей папке и сохранить результаты в файле, как указано выше, и отобразить его два раза. Но эффекта нет. Всего один вывод об ошибке в консоли. Это связано с тем, что эта ошибка исходит из стандартного потока ошибок ls. Мы удалили файл, чтобы освободить место « /tmp/log ».

Перенаправление потоков

Можно манипулировать стандартными потоками: перенаправлять их или использовать каналы для их обработки.

тело скрипта:

#!/usr/bin/env bash

ls /not/existed_path 2>/dev/null
ls * /not/existed_path > /tmp/log 2> /tmp/err_log
cat /tmp/log
rm /tmp/log /tmp/err_log

Скрипт, работающий в консоли:

В этой команде мы используем действие перенаправления « > ». /dev/null — это файл завершения потока для удаления любого ввода без обработки. Предложение « 2> » перенаправляет стандартный поток вывода в файл. Теперь можно просмотреть вывод и ошибки позже, вызвав «cat /tmp/log» или «cat /tmp/err_log».

Отменить вывод

В некоторых случаях полезно удалить все выходные данные приложения (чтобы сэкономить место для хранения или удалить неважные источники данных для анализа). Мы должны помнить, что мы должны отключать звук не только стандартного потока вывода, но и стандартного потока ошибок . Давайте посмотрим на примере, как это работает.

тело скрипта:

#!/usr/bin/env bash

ls . unexisted_file > /dev/null
ls . unexisted_file > /dev/null 2>&1

Скрипт, работающий в консоли:

В обеих строках скрипта стандартный поток вывода перенаправляется в файл /dev/null . Этот файл не является самим файлом: это специальное устройство Linux с файловым интерфейсом. У него есть функция записи, доступная из оболочки (которая ничего не делает). Таким образом, все входные данные просто отбрасываются. А вот вторая строка скрипта отличается от первой: « 2>&1 » в конце команды. Эти символы говорят о том, что второй стандартный поток ( стандартный поток ошибок ) должен быть направлен на первый стандартный поток ( стандартный выходной поток ) для этой команды. Именно поэтому в консоли мы видим только одно сообщение об ошибке — во второй командной строке скрипта стандартный поток ошибок перенаправляется на завершающее файловое устройство /dev/null в конце.

Конвейерные потоки

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

тело скрипта:

#!/usr/bin/env bash

touch /tmp/a
ls /tmp/* 2> /dev/null | grep -w a
rm /tmp/a

Скрипт, работающий в консоли:

Здесь мы собираем листинг и устанавливаем стандартный поток вывода « ls » в качестве стандартного потока ввода grep , используя « | ». На « 2> » поток стандартных ошибок перенаправляется в файл завершения. Выходной поток grep содержит имя файла « /tmp/a » из списка, которое соответствует шаблону grep.

Здесь документ

Здесь документ представляет собой вариант перенаправления для заполнения входного потока приложения собственным способом:

application << delimiter
some useful text
delimiter

тело скрипта:

#!/usr/bin/env bash

grep perfect << EOF
    PERFECT STORY
    past perfect
    is it Perfect?
EOF

Скрипт работает в консоли

Как мы видим, только вторая строка из 3-х строк текста для стандартного потока ввода grep, разделенных «EOF», соответствует шаблону grep : поэтому она передается в стандартный поток вывода grep и отображается в консоли дальше.

Вывод

Есть несколько стандартных утилит для работы с потоками ( cat , cut , grep , sed , tr , …). Вы можете использовать его или написать свой собственный, чтобы создать конвейер обработки потока данных, который будет соответствовать вашим потребностям. Под капотом он будет работать с stdin , stdout , stderr .