Управляем потоками в Linux: от stdin до stderr

от автора

Привет, Хабр!

Сегодня рассмотрим работу с потоками в 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 в апреле:


ссылка на оригинал статьи https://habr.com/ru/articles/894854/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *