В этом параграфе мы познакомимся с тем, как команды в терминале обмениваются данными. Каждая команда может получать входные данные (stdin), отправлять результат работы (stdout) и сообщения об ошибках (stderr).
Мы научимся управлять этими потоками с помощью перенаправлений и разберёмся, что такое код возврата команды и как его использовать для проверки успеха или ошибки.
Ключевые вопросы параграфа
- Что такое стандартные потоки
stdin,stdoutиstderr? - Что такое перенаправление потока и для чего оно нужно?
- Как и для чего комбинировать разные перенаправления?
- Что такое коды возврата команд и как их можно проверить?
- Что за файл
/dev/nullи зачем он нужен?
Что такое потоки ввода-вывода
В предыдущем параграфе мы использовали команды вроде grep, wc и diff, чтобы анализировать данные и сравнивать результаты.
Но все эти команды не просто «что-то делают» — они принимают данные, обрабатывают их и отдают результат обратно в терминал.
Именно этот процесс передачи данных называется вводом и выводом (англ. input/output или просто I/O).
Каждая команда в Linux общается с внешним миром через три «канала связи» — потоки ввода-вывода.
Они встроены в систему и существуют для каждой программы по умолчанию:
|
Поток |
Название |
Описание |
|
|
Стандартный ввод |
То, откуда команда получает данные (обычно клавиатура, но может быть файл или вывод другой команды) |
|
|
Стандартный вывод |
То, куда команда отправляет результат своей работы (обычно экран терминала) |
|
|
Стандартный поток ошибок |
То, куда команда пишет сообщения об ошибках (тоже обычно экран, но отдельно от основного вывода) |
Можно представить это так:

Эти потоки работают одновременно, но независимо друг от друга. Это позволяет, например, записывать результат работы команды в один файл, а ошибки — в другой.
Пример 1. Команда без ошибок
1alice@ubuntu:~$ echo "Hello, world!"
2Hello, world!
Команда echo получает текст "Hello, world!" через свои аргументы, выводит его в стандартный поток вывода (stdout), и текст попадает на экран.
Всё прошло успешно — ошибок нет, поэтому в поток stderr ничего не попало.
Пример 2. Команда с ошибкой
1alice@ubuntu:~$ cat missing.txt
2cat: missing.txt: No such file or directory
cat пыталась прочитать файл missing.txt, но не нашла его.
Текст ошибки No such file or directory — это не просто сообщение на экране, это вывод в поток stderr, отдельный от основного вывода stdout.
Почему это важно
Пока команды просто показывают результат на экране, разницы между stdout и stderr не видно.
Но когда вы начнёте перенаправлять вывод в файлы, передавать результаты другим командам или писать скрипты, разделение этих потоков становится критически важным.
Например:
- вы можете сохранить результат работы программы в файл, но ошибки записать в отдельный;
- или объединить оба потока, чтобы получить полный журнал выполнения;
- или вовсе скрыть ошибки, если они вам не нужны.
Это и есть основа управления вводом и выводом, с которой мы познакомимся дальше.
Перенаправление вывода
Ранее мы уже применяли эту механику, когда вызывали команду и записывали результат в файл c помощью оператора >.
Это частый случай перенаправления вывода. Он нужен, например, чтобы потом проанализировать файл, отправить коллеге или просто не потерять после закрытия терминала.
И точно так же можно перенаправлять вывод ошибок и делать многие другие вещи. Разберём перенаправления подробнее.
Перенаправление стандартного вывода (stdout)
Чтобы записать результат работы команды в файл вместо экрана, используется символ >:
1alice@ubuntu:~$ echo "Hello" > output.txt
Теперь ничего не появилось на экране, потому что текст "Hello" был направлен не в терминал, а в файл output.txt.
Если откроем его, увидим наш результат:
1alice@ubuntu:~$ cat output.txt
2Hello
Символ > создаёт новый файл, если его не было, или перезаписывает существующий, если он уже есть.
Ранее мы создавали новый файл таким образом в качестве альтернативы команде touch.
Запустим команду снова с другим текстом:
1alice@ubuntu:~$ echo "World" > output.txt
2alice@ubuntu:~$ cat output.txt
3World
Видим, что старое содержимое файла исчезло — на его месте теперь только слово World.
Но мы можем не перезаписывать, а добавлять текст в конец файла, и для этого используется двойная стрелка >>:
1alice@ubuntu:~$ echo "Hello" > output.txt
2alice@ubuntu:~$ echo "World" >> output.txt
3alice@ubuntu:~$ cat output.txt
4Hello
5World
Теперь мы видим в файле обе строки, которые мы записали.
Запомните:
>— создаёт или перезаписывает файл,>>— добавляет текст в конец файла.
Перенаправление ошибок (stderr)
По умолчанию и обычный вывод (stdout), и ошибки (stderr) печатаются на одном и том же экране.
Но Linux позволяет направлять их в разные файлы.
Для этого используется 2> — цифра 2 обозначает поток ошибок (stderr), а 1 — стандартный вывод (stdout).
Попробуем пример:
1alice@ubuntu:~$ ls /root 2> errors.log
Если у пользователя нет прав доступа к каталогу /root, команда ls выведет сообщение об ошибке. Но теперь ошибка окажется в файле errors.log, а не на экране.
1alice@ubuntu:~$ cat errors.log
2ls: cannot open directory '/root': Permission denied
При этом, если бы в выводе были обычные данные (stdout), они всё равно появились бы в терминале.
Разделение вывода и ошибок
Часто программы выводят и результат, и ошибки одновременно.
Чтобы их не перепутать, можно записать их в разные файлы:
1alice@ubuntu:~$ ls -l /var /root > output.log 2> errors.log
Теперь:
- всё, что программа выводит «нормально» (
stdout), попадёт вoutput.log; - всё, что она пишет как ошибку (
stderr), окажется вerrors.log.
Можем проверить наши файлы, чтобы в этом убедиться:
1alice@ubuntu:~$ cat output.log
2/var:
3total 40
4drwxr-xr-x 2 root root 4096 Oct 6 14:43 backups
5drwxr-xr-x 15 root root 4096 Sep 23 15:17 cache
6drwxrwsrwt 2 root root 4096 Jun 5 12:55 crash
7drwxr-xr-x 43 root root 4096 Sep 23 15:17 lib
8drwxrwsr-x 2 root staff 4096 Apr 22 2024 local
9lrwxrwxrwx 1 root root 9 Jun 5 12:53 lock -> /run/lock
10drwxrwxr-x 9 root syslog 4096 Oct 19 00:00 log
11drwxrwsr-x 2 root mail 4096 Jun 5 12:53 mail
12drwxr-xr-x 2 root root 4096 Jun 5 12:53 opt
13lrwxrwxrwx 1 root root 4 Jun 5 12:53 run -> /run
14drwxr-xr-x 4 root root 4096 Jun 5 12:53 spool
15drwxrwxrwt 9 root root 4096 Oct 21 07:45 tmp
1alice@ubuntu:~$ cat errors.log
2ls: cannot open directory '/root': Permission denied
Разделение потоков вывода — одна из самых практичных возможностей Linux.
Оно позволяет собирать чистые логи, анализировать ошибки отдельно и не терять полезную информацию в большом потоке сообщений.
Давайте проверим, как вы разобрались с потоками и перенаправлениями вывода:
Объединение потоков
Многие программы выводят и обычные результаты, и сообщения об ошибках. Мы уже знаем, что это два разных потока (stdout и stderr), поэтому если перенаправить только стандартный вывод
1command > output.log
— ошибки всё равно появятся на экране.
Чтобы сохранить всё в одном месте, направьте поток ошибок туда же, куда уже уходит обычный вывод. Это называется объединением потоков.
Классический способ
Для объединения потоков используется следующая запись:
1command > output.log 2>&1
Разберём, что здесь происходит, по шагам:
> output.log— направляет стандартный вывод (stdout, поток1) в файлoutput.log.2>&1— говорит системе: «Отправь поток ошибок (stderr, поток2) туда же, куда сейчас направлен поток1».
Другими словами, всё, что команда напишет — и обычные сообщения, и ошибки, — попадёт в один и тот же файл.
Пример:
1alice@ubuntu:~$ ./script.sh > output.log 2>&1
Теперь в output.log окажутся и успешный вывод, и ошибки:
1alice@ubuntu:~$ cat output.log
2Starting process...
3Warning: configuration file missing
4Process completed successfully
Это удобно, когда вы хотите сохранить весь вывод программы для последующего анализа, например, при отладке скриптов или программ.
Современный короткий синтаксис
Начиная с Bash 4 появился более короткий способ объединить потоки:
1alice@ubuntu:~$ ./script.sh &> output.log
Он делает то же самое, что и > output.log 2>&1, но выглядит аккуратнее.
А если нужно добавлять в существующий файл, используйте &>>:
1alice@ubuntu:~$ ./script.sh &>> output.log
Обратите внимание: &> и &>> работают только в Bash. Если вы используете другие оболочки (например, sh или dash), делайте классическую запись > file 2>&1.
Один важный нюанс
Может показаться, что записи > output.log 2> output.log и > output.log 2>&1 эквивалентны.
Но это не так.
Дело в том, что Bash открывает файлы по порядку, слева направо. Когда вы пишете:
1command > output.log 2> output.log
— Bash делает следующее:
- Открывает
output.logдля стандартного вывода (stdout). - Открывает тот же файл ещё раз отдельно для ошибок (
stderr). - Запускает команду.
В результате у вас два разных файловых дескриптора, оба указывают на один и тот же файл, но с разными указателями позиции записи.
Если программа печатает что-то в оба потока почти одновременно, файл может получиться перемешанным или с обрывками строк, например:
1Error: missing file
2Output: process sError: retrying...
3uccessful
То есть порядок строк и даже целостность данных может нарушиться — файл не будет отражать реальный ход вывода.
А вот 2>&1 создаёт общее соединение потоков: оба потока используют один и тот же файловый дескриптор, а значит, пишут в файл согласованно и в правильной последовательности.
Когда стоит объединять потоки
Объединение потоков полезно, когда:
- вы хотите получить единый лог выполнения команды;
- вам нужно проанализировать ошибки позже, без визуального шума в терминале;
- команда запускается в фоне (через
&) и её вывод нужно сохранить целиком.
Но объединение потоков не всегда удобно — чаще полезнее разделять их, чтобы не смешивать обычные результаты и сообщения об ошибках.
Выбор зависит от задачи: для отладки — объединяем, для анализа — разделяем.
Давайте попробуем составить команду, которая скопирует каталог /var/log в /mnt/backup, а результат выполнения и ошибки копирования сохранит в файл backup.log:
Перенаправление ввода (stdin)
Мы уже научились управлять выводом команд — направлять результаты в файлы или объединять их в логи.
Теперь посмотрим на процесс с другой стороны: как команды получают данные для работы.
Большинство программ в Linux ждут ввод из стандартного потока ввода (stdin).
Обычно это клавиатура: вы печатаете текст, программа его читает.
Но мы можем заменить клавиатуру файлом, чтобы программа читала данные оттуда автоматически.
Для этого используется символ <.
Пример 1. Подать данные из файла
Команда sort сортирует строки текста.
Мы можем подать ей файл напрямую в качестве аргумента:
1alice@ubuntu:~$ sort names.txt
Или сделать то же самое с использованием <:
1alice@ubuntu:~$ sort < names.txt
Результат будет одинаковым. sort возьмёт содержимое names.txt из потока ввода (stdin), отсортирует и выведет результат на экран (stdout):
1Alice
2Bob
3Charlie
4Diana
Разницы на экране нет, но < становится особенно полезен, когда вы строите цепочки команд или работаете со скриптами, ведь он позволяет гибко заменять источник данных.
Пример 2. Подать файл в wc
Команда wc -l подсчитывает количество строк во входных данных.
Её можно использовать с файлом в качестве аргумента:
1alice@ubuntu:~$ wc -l data.txt
242 data.txt
А можно подать файл через стандартный ввод:
1alice@ubuntu:~$ wc -l < data.txt
242
Разница в том, что при использовании < команда видит только поток данных, без имени файла.
Поэтому в выводе остаётся только число 42, без лишней информации.
Пример 3. cat < file.txt
Если подать файл через <, команда cat просто прочитает его содержимое из потока ввода и выведет на экран:
1alice@ubuntu:~$ cat < file.txt
2Hello from stdin!
Эта запись полностью эквивалентна:
1alice@ubuntu:~$ cat file.txt
2Hello from stdin!
Здесь < просто заменяет «чтение из файла по имени» «чтением из стандартного ввода».
Это мелочь, но в скриптах иногда именно такая форма бывает удобнее, особенно если имя файла хранится в переменной или подставляется автоматически.
Как это работает «под капотом»
Когда вы вводите < file.txt, оболочка открывает файл и подключает его как источник данных для команды.
Команда «думает», что данные приходят с клавиатуры, хотя на самом деле они читаются из файла.
Можно представить это схематично:

Таким образом, < — это операция, обратная >:
>отправляет данные из команды в файл,<подаёт данные из файла в команду.
Примеры на практике
|
Задача |
Команда |
Что происходит |
|
Отсортировать строки из файла |
|
Содержимое файла читается как ввод |
|
Подсчитать строки в файле |
|
|
|
Просмотреть содержимое файла |
|
Эквивалентно |
|
Передать данные из одного файла в другой |
|
Чтение из |
Использование < не обязательно, но удобно, когда вы:
- хотите писать команды в универсальной форме, не привязываясь к конкретным файлам;
- работаете с перенаправлениями в обе стороны (например,
cat < input.txt > output.txt); - хотите автоматизировать передачу данных между программами — в конвейерах, циклах или скриптах.
Давайте попробуем составить команду, которая считает количество строк в файле data.txt, подавая его через стандартный ввод, а результат сохранит в файл result.txt:
Примеры сочетаний перенаправлений
Мы уже познакомились с каждым типом перенаправления по отдельности. Теперь посмотрим, как их можно комбинировать.
В Linux можно соединять разные направления потоков как конструктор: перенаправлять ввод и вывод, разделять ошибки, объединять их или добавлять данные в существующие файлы.
Эта гибкость делает терминал невероятно мощным инструментом: вы можете точно контролировать, куда уходит каждый байт информации.
|
Задача |
Команда |
Что происходит |
|
Записать результат в файл |
|
Основной вывод ( |
|
Добавить результат к концу файла |
|
Данные добавляются в конец файла ( |
|
Сохранить только ошибки |
|
Ошибки ( |
|
Разделить потоки |
|
Основной вывод ( |
|
Объединить всё в один лог |
|
И обычный вывод, и ошибки сохраняются в одном файле |
|
Подать ввод из файла |
|
Файл |
Как это запомнить
Если попытаться визуализировать всё, что происходит, картина будет примерно такая:

1>— основной поток (stdout),2>— поток ошибок (stderr),<— поток ввода (stdin).
А комбинации вроде 2>&1 просто говорят оболочке: «Отправь ошибки туда же, куда направлен обычный вывод».
Коды возврата команд
Когда вы запускаете любую команду в терминале, она не только выводит результат на экран, но и сообщает системе, чем всё закончилось.
Это делается при помощи кода возврата (англ. exit code).
Код возврата — это число, которое программа передаёт обратно оболочке после завершения.
Оно показывает, успешно ли выполнена команда, и используется для автоматической проверки результата в скриптах и цепочках команд.
|
Код |
Значение |
Что это значит |
|
|
Успех |
Команда выполнилась без ошибок |
|
|
Общая ошибка |
Что-то пошло не так (универсальный код) |
|
|
Ошибка аргументов |
Команде переданы неверные параметры или файл не найден |
|
|
Команда найдена, но не может быть выполнена |
Нет прав или это не исполняемый файл |
|
|
Команда не найдена |
Bash не смог найти указанную команду |
|
|
Прерывание пользователем (Ctrl + C) |
Выполнение остановлено вручную |
Эти значения не жёстко фиксированы: разные программы могут использовать свои коды ошибок, но принцип остаётся тем же: 0 всегда означает «всё хорошо».
Как проверить код возврата
После выполнения любой команды вы можете узнать её код при помощи специальной переменной $?.
Эта переменная всегда хранит код последней выполненной команды.
Пример успешной команды:
1alice@ubuntu:~$ ls /home
2alice
3bob
4carol
5alice@ubuntu:~$ echo $?
60
Код 0 говорит о том, что последняя команда (а именно ls /home) выполнилась успешно.
Если команда завершилась с ошибкой:
1alice@ubuntu:~$ ls /notfound
2ls: cannot access '/notfound': No such file or directory
3alice@ubuntu:~$ echo $?
42
Теперь код возврата равен 2. Это значит, что последняя команда выполнилась неуспешно, в нашем случае ls не смогла найти указанный каталог.
Даже если вы не видите вывода на экран, Bash всегда хранит код возврата последней команды. Это ключевой элемент для работы логических операторов и условий в скриптах, о них мы поговорим в следующем параграфе.
/dev/null — «чёрная дыра»
Иногда команда работает правильно, но её вывод совершенно не нужен.
Например, вы запускаете скрипт, который печатает десятки строк служебной информации, а вам важно только то, что он завершился успешно.
В таких случаях можно «утилизировать» весь вывод, не загромождая терминал.
Для этого в Linux существует специальное место — /dev/null.
Что это такое
/dev/null — это виртуальный файл, который не хранит данные. Всё, что вы в него запишете, навсегда исчезает.
Можно представить его как «чёрную дыру», которая принимает любые данные, но никогда их не возвращает.
Попробуйте:
1alice@ubuntu:~$ echo "Hello" > /dev/null
Ничего не произошло, и это нормально.
Файл /dev/null просто «поглотил» строку Hello, не записав её никуда.
Полное подавление вывода
Если вы хотите, чтобы команда выполнялась молча, можно перенаправить в /dev/null и стандартный вывод (stdout), и ошибки (stderr):
1alice@ubuntu:~$ ls -l /var /root > /dev/null 2>&1
Разберём, что делает эта запись:
> /dev/null— отправляет обычный вывод (stdout) в «чёрную дыру».2>&1— направляет поток ошибок (stderr) туда же, куда сейчас направленstdout, то есть тоже в/dev/null.
В результате команда выполнится, но на экране не появится ни одной строки, даже если произойдут ошибки.
Примеры использования
|
Цель |
Команда |
Что происходит |
|
Скрыть успешный вывод, но оставить ошибки |
|
Ошибки всё ещё видны на экране |
|
Скрыть только ошибки |
|
Основной результат остаётся |
|
Скрыть всё |
|
Команда выполняется полностью в тишине |
Когда это полезно
Например, для автоматизации: скрипты и cron-задачи часто перенаправляют вывод в /dev/null, чтобы не засорять логи.
Также в тестировании можно проверять код возврата без вывода, как в примере ниже:
1grep "ERROR" app.log > /dev/null
2echo $?
А ещё при отладке удобно временно «заглушить» шумную команду, не влияя на логику скрипта.
В Linux есть и другие специальные устройства в /dev/, например:
/dev/zero— бесконечный поток нулей (используется для тестирования и создания файлов);/dev/random— поток случайных данных.
Но /dev/null — самое известное и часто используемое: оно буквально говорит системе: «Забудь про это».
Давайте проверим, как вы разобрались с сочетаниями перенаправлений:
Что дальше
Теперь, когда вы разобрались, как команды в Linux принимают данные, выводят результаты и сообщают об ошибках, пора перейти к очередному важному шагу — научиться связывать команды между собой.
В следующем параграфе вы узнаете:
- Что такое пайп (
|) и как он передаёт вывод одной команды в другую. - Как строить простые цепочки команд и сложные конвейеры.
- Как управлять последовательностью выполнения команд с помощью логических операторов.
- И почему принцип KISS (Keep It Simple, Stupid) лежит в основе философии Unix и конвейеров команд.
После этого вы сможете объединять инструменты Linux в единые цепочки, автоматизировать проверки и анализ данных — от простых фильтров до целых сценариев.
А пока закрепите материал на практике:
- Отметьте, что урок прочитан, при помощи кнопки ниже.
- Пройдите мини-квиз, чтобы проверить, насколько хорошо вы разобрались с потоками.
- Перейдите к задачам этого параграфа и потренируйтесь.
- Перед этим — загляните в короткий гайд о том, как работает система проверки.
Ключевые выводы по параграфу
- Каждая команда в Linux работает с тремя стандартными потоками:
stdin— стандартный ввод (обычно клавиатура, но может быть файл или другая команда);stdout— стандартный вывод (результат работы команды, по умолчанию — на экран);stderr— поток ошибок (сообщения о сбоях и предупреждениях).
- Перенаправления позволяют управлять этими потоками:
>— записать результат в файл (перезаписать его);>>— добавить результат в конец файла;2>— записать ошибки (stderr) в отдельный файл;> out.log 2> err.log— разделить основной и ошибочный выводы;> all.log 2>&1или&> all.log— объединить оба потока в один файл;< file.txt— подать файл как входные данные (stdin).
- Комбинируя перенаправления, можно создавать сложные цепочки: сохранять результаты в один файл, ошибки в другой или подавлять вывод вовсе.
- Коды возврата (или exit-коды) — способ, которым команда сообщает, успешно ли она завершилась:
0— успех;- значения, отличные от
0, — ошибка (например:2— файл не найден,127— команда не найдена).
- Код возврата можно проверить через
$?, они часто используются в скриптах и цепочках команд. /dev/null— «чёрная дыра» Linux: всё, что туда записано, исчезает. Используется для подавления ненужного вывода (> /dev/null 2>&1).
