Перенаправление "2>&1", ">/dev/null" или потоки вывода в Unix (bash/sh)




6846
Перенаправление "2>&1", ">/dev/null" или потоки вывода в Unix (bash/sh)
Рейтинг 9.67 из 10. Голосов: 187
Ваша оценка:
Потоки вывода

Сообщения скриптов выводятся во вполне определенные потоки - потоки вывода. Таким образом то, что мы выводим через
echo "Hello, world!"
не просто выводится на экран, а, с точки зрения системы, а конкретно - командных интерпретаторов sh и bash - выводится через определенный поток вывода. В случае echo - поток под номером 1 (stdout), с которым ассоциирован экран.

Некоторые программы и скрипты так-же используют другой поток вывода - под номером 2 (stderr). В него они выводят сообщения об ошибках. Благодаря этому можно раздельно выхватывать из потоков обычные информационные сообщения и сообщения об ошибках и направлять и обрабатывать их раздельно.

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



Что такое ">somefile"

Такой записью в Unix (в интерпретаторах bash и sh) указывается перенаправление потоков вывода.


В следующем примере мы перенаправим все информационные (обычные) сообщения команды ls в файл myfile.txt, получив таким образом в этом файле просто список ls:
$ ls > myfile.txt

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


Однако давайте сделаем заведомо ошибочную операцию:
$ ls /masdfasdf > myfile.txt

И что случится? Т.к. директории masdfasdf в корне файловой системы не существует (я так предполагаю - вдруг у Вас есть?), то команда ls сгенерирует ошибку. Однако вывалит эту ошибку она уже не через обычный поток stdout (1), а через поток ошибок stderr (2). А перенаправление задано лишь для stdout ("> myfile.txt").

Т.к. поток stderr (2) мы никуда не перенаправили - сообщение об ошибке появится на экране и НЕ появится в файле myfile.txt


А теперь давайте выполним команду ls так, чтобы информационные данные записались в файл myfile.txt, а сообщения об ошибках - в файл myfile.err, при этом на экране во время выполнения не появится ничего:
$ ls >myfile.txt 2>myfile.err

Здесь нам встречается впервые указание номера потока в качестве перенаправления. Запись "2>myfile.err" указывает, что поток с номером 2 (stderr) нужно перенаправить в файл myfile.err.

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


2>&1

Нередко в скриптах можно встретить такую запись. Она означает: "Поток с номером 2 перенаправить в поток с номером 1", или "Поток stderr - направить через поток stdout". Т.е. все сообщения об ошибках мы направляем через поток, через который обычно печатаются обычные, не ошибочные сообщения.

$ ls /asfasdf 2>&1

А вот еще пример, в котором все сообщения перенаправляются в файл myfile.txt:
$ ls /asfasdf >myfile.txt 2>&1

При этом все сообщения, как об ошибках, так и обычные, будут записаны в myfile.txt, т.к. поток stdout мы сначала перенаправлили в файл, а потом указали, что ошибки нужно вываливать в stdout - соответственно, в файл myfile.txt


/dev/null

Однако иногда нам нужно просто скрыть все сообщения - не сохраняя их. Т.е. просто блокировать вывод. Для этого служит виртуальное устройство /dev/null. В следующем примере весь вывод обычных сообщений команды ls мы направим в /dev/null:

$ ls > /dev/null

На экране не будет отображено ничего, кроме сообщений об ошибках. А в следующем примере - и сообщения об ошибках будут блокированы:
$ ls > /dev/null 2>&1

Причем эта запись эквивалентна записи вида:
$ ls >/dev/null 2>/dev/null

А в следующем примере мы заблокируем только сообщения об ошибках:
$ ls 2>/dev/null
Заметьте, что здесь уже нельзя указывать "2>&1", т.к. поток (1) не перенаправлен никуда и в таком случае сообщения об ошибках будут банально вывалены на экран.


Что первее - яйцо или курица?

Я приведу Вам здесь 2 примера.

Пример 1)
$ ls >/dev/null 2>&1

Пример 2)
$ ls 2>&1 >/dev/null

С виду - от перестановки мест слогаемых сумма не меняется. Но порядок указателей перенаправления играет роль!

Дело в том, что интерпретаторы читают и применяют перенаправления слева направо. И сейчас мы разберем оба примера.


Пример 1

1) ">/dev/null" - мы направляем поток 1 (stdout) в /dev/null. Все сообщения, попадающие в поток (1) - будут направлены в /dev/null.

2) "2>&1" - мы перенаправляем поток 2 (stderr) в поток 1 (stdout). Но, т.к. поток 1 уже ассоциирован с /dev/null - все сообщения все-равно попадут в /dev/null.

Результат: на экране - пусто.


Пример 2

1) "2>&1" - мы перенаправляем поток ошибок stderr (2) в поток stdout (1). При этом, т.к. поток 1 по-умолчанию ассоциирован с терминалом - сообщения об ошибках мы успешно увидим на экране.

2) ">/dev/null" - а уже здесь мы перенаправляем поток 1 в /dev/null. И обычные сообщения мы не увидим.

Результат: мы будем видеть сообщения об ошибках на экране, но не будет видеть обычные сообщения.


Вывод: сначала перенаправьте поток, а потом на него ссылайтесь.

Вопросы и ответы

Vadim
2020-02-08 17:02:10
Спасибо вам большое. Это самый внятный туториал!
Artem
2020-04-11 14:00:06
Очень просто и доходчиво
michael
2020-10-14 08:38:41
Вот это руководство в топ!
Guest
2020-10-31 11:45:05
Мне всё понятно и максимально информативно

Оставить комментарий

ответить