Привет, Хабр!
Сегодня рассмотрим работу с потоками в Linux: stdin, stdout, stderr и, конечно, все эти оператора редиректа.
Файловые дескрипторы: 0, 1, 2
Для начала поговорим о том, что такое эти загадочные числа 0, 1 и 2. Если кажется, что это просто архаичные обозначения — вы глубоко заблуждаетесь. В Unix всё — буквально всё — является файлом.
-
0 — это стандартный ввод
stdin
. Сюда поступают команды, нажатия клавиш и всякое такое. -
1 — это стандартный вывод
stdout
. Все, что вы видите в терминале (например, результат выполнения команды), идет сюда. -
2 — это стандартный поток ошибок
stderr
. Любая ошибка, предупреждение или критическая ситуация — всё это тут.
Если провести аналогию с офисом, то 0 — это входящие звонки, 1 — обычная переписка, а 2 — сигнал тревоги, когда что‑то идёт не так.
Редиректы
Эти операторы позволяют направить вывод туда, куда вам нужно, будь то файл, команда или даже другой поток.
Оператор >
Этот оператор — рабочий конвейер, который берет stdout и перенаправляет его в указанный файл.
#!/bin/bash # Записываем результат выполнения команды в файл output.txt echo "Привет, stdout!" > output.txt
Здесь, если файл уже существует, он будет перезаписан. А если нужно пришить новый вывод к уже существующему файлу, используйте оператор >>
.
Оператор 2>
Когда дело доходит до ошибок, то помогает оператор 2>
. Он берет stderr
и пишет его в отдельный файл. Может пригодится, если отделить «хорошие» сообщения от «бесполезного» шума ошибок:
#!/bin/bash # Пытаемся выполнить команду, которая гарантированно завалится ls /some/nonexistent/dir 2> error.log
В результате получите файл с ошибкой, а основной вывод останется чистым.
Оператор &>
А вот &> — маст‑хэв для тех, кто хочет всё в одном флаконе: и stdout, и stderr вместе.
#!/bin/bash # Сохраняем и stdout, и stderr в файл combined.log ./some_script.sh &> combined.log
Оператор 1>&2
Если хочется, чтобы обычный вывод шел туда, куда уже направлены ошибки, то оператор 1>&2 решает эту задачу:
#!/bin/bash # Отправляем stdout в stderr (ну, бывает, что так удобнее для логирования) echo "Это сообщение пойдет в stderr" 1>&2
Here-strings <<< и Here-documents <
Представьте, что вам надо передать строку или блок текста в качестве ввода для команды. Вот тут на помощь приходят:
-
Here‑string (<<<): позволяет передать строку напрямую в команду.
# Передаем строку в grep через stdin grep "pattern" <<< "Некоторый текст с pattern внутри"
-
Here‑document (<<EOF): для многострочного ввода.
cat <<EOF Привет, мир! Это многострочный ввод, который позволяет задать сразу несколько строк. EOF
Утилиты tee, read и exec
Команда tee
Команда tee позволяет одновременно записывать данные в файл и выводить их на экран.
#!/bin/bash # Ловим вывод команды и сразу же его записываем в log.txt, не упуская ничего echo "Запускаем процесс..." | tee log.txt
А если хочется фильтровать вывод, то можно комбинировать с grep
:
#!/bin/bash # Логирование и фильтрация ошибок одновременно ./run_process.sh 2>&1 | tee process.log | grep "ERROR"
Команда read
Как часто мы сталкивались с ситуацией, когда скрипт зависает, ожидая ввода? Вот тут на помощь приходит команда read
с таймаутом.
#!/bin/bash # Запрашиваем ввод с таймаутом в 5 секунд echo "Введите что-нибудь (у вас 5 секунд):" if read -t 5 userInput; then echo "Вы ввели: $userInput" else echo "Время вышло! Давайте продолжим без вашего участия." fi
Команда exec
Если хотите, чтобы скрипт был настолько чист, что все будущие сообщения автоматически шли в лог‑файл, то тут полезна команда exec
:
#!/bin/bash # Перенаправляем весь stdout и stderr в файл с помощью exec и tee exec &> >(tee -a script.log) echo "Эта строка уже попадет в лог!"
Кстати, exec можно использовать и для замены текущего процесса на другой — это мощно, но требует осторожности. Когда вы запускаете exec ls -l
, скрипт умирает и превращается в ls.
Как применить редиректы на практике
Отдельная запись stdout и stderr
Допустим, нужно разделить обычный вывод и ошибки, чтобы потом анализировать их независимо:
#!/bin/bash # Файл: separate_logs.sh # Записываем stdout в out.log, а stderr в err.log ./some_command.sh 1> out.log 2> err.log
Логирование пайплайнов для отладки
Иногда пайплайн настолько сложен, что хочется видеть, как данные проходят через каждое звено:
#!/bin/bash # Файл: debug_pipeline.sh # Пайплайн с промежуточным логированием cat input.txt | tee step1.log | grep "filter_pattern" | tee step2.log | sort | tee final.log
Каждый шаг логируется, и если что‑то пойдет не так — вы сразу увидите, на каком этапе данные потерялись или искажались.
Ввод с таймаутом и повторами
Иногда нужно не только запросить ввод, но и дать пользователю несколько шансов, прежде чем скрипт сдастся:
#!/bin/bash # Файл: read_retry.sh # Несколько попыток ввода с таймаутом TIMEOUT=5 TRIES=3 attempt=1 while [ $attempt -le $TRIES ]; do echo "Попытка $attempt/$TRIES: введите пароль (5 секунд на ответ):" if read -t $TIMEOUT password; then if [ "$password" == "secret" ]; then echo "Доступ разрешен. Добро пожаловать!" break else echo "Неверный пароль. Попробуйте еще раз." fi else echo "Таймаут! Вы слишком медлили." fi attempt=$((attempt + 1)) done if [ $attempt -gt $TRIES ]; then echo "Слишком много попыток. Доступ запрещен." fi
Всем начинающим администраторам Linux крайне рекомендую посетить открытые уроки, которые пройдут в Otus в апреле:
-
2 апреля: Вся правда о рынке труда или как быть востребованным в современных реалиях. Узнать подробнее
-
17 апреля: Фильтрация трафика в iptables. Узнать подробнее
ссылка на оригинал статьи https://habr.com/ru/articles/894854/
Добавить комментарий