Тариф успешно добавлен в корзину
В корзину
url image

Часть 3. Bash-скрипты. Перенаправление ввода и вывода

Перед чтением этой статьи рекомендуем ознакомиться с предыдущими публикациями из серии Основы написания bash-скриптов:

  1. Знакомство с bash
  2. Часть 1. Bash-скрипты. Основные команды и рекомендации
  3. Часть 2. Bash-скрипты. Переменные, комментарии, пользовательский ввод

В третьей статье серии о bash-скриптах мы расскажем, что такое потоки данных в bash, и рассмотрим операторы перенаправления ввода и вывода. 

Введение

При работе с оболочкой bash часто требуется передать данные от одной утилиты к другой. Например, можно в одной цепочке команд при помощи grep найти в каталоге файлы с определённой последовательностью символов, командой mv в один приём переименовать все эти файлы и переместить в отдельный каталог. Данные между командами передают при помощи операторов перенаправления потоков и управляющих операторов. 

В этой статье рассмотрены операторы перенаправления ввода и вывода, в следующей — конвейеры, here-документы и управление заданиями.

Что такое потоки данных в bash

Когда пользователь взаимодействует с компьютером через командную строку, данные делятся на три потока: 

  • стандартный ввод, stdin;
  • стандартный вывод, stdout;
  • стандартная ошибка, stderr.

За каждым потоком закреплён идентификатор, называемый файловым дескриптором.

Файловый дескриптор — это системный объект, через который программы ссылаются на файлы или на другие ресурсы (например, конвейеры, устройства или терминалы). Он указывает на источник данных или место их записи.

Название

Значение

Получение или отображение

Номер файлового дескриптора

stdin 

Вводимые данные

Обычно вводится через клавиатуру. Может перенаправляться в другие точки через конвейер.

0

stdout 

Выводимые данные

Обычно выводятся в терминал. Могут перенаправляться в другие точки через конвейер

1

stderr

Диагностические и отладочные сообщения

2

Точки ввода и вывода можно менять, чтобы компьютер получал информацию не со стандартного ввода и выводил не на стандартный вывод. Такое изменение называется перенаправлением.

В следующих разделах будут показаны примеры использования файловых дескрипторов для перенаправления потоков.

Перенаправление вывода >

Оператор > перенаправляет по указанному пути содержимое стандартного вывода команды и сообщений об ошибках. Не все команды генерируют поток stdout: например, у команд synctrue и false, mount и umount и многих других он не предусмотрен. 

Операторы — спецсимволы или их последовательности, производящие операции с командами, потоком управления, логикой, арифметикой и сравнением строк.

Cогласно Руководству GNU по обоочке bash к операторам относят управляющие операторы и операторы перенаправления.

Операторы управления — это следующие токены (последовательности символов): перенос строки, ||, &&, &, ;, ;;, |, (, ) и другие. Многие из них мы рассмотрим в статьях этой серии.

Операторы перенаправления >, <, >> и другие рассмотрены в этой и следующей статьях.

Возьмём результат выполнения команды date:

$ date
Чт 18 апр 2024 14:58:52 +04

Содержимое потока stdout можно перенаправить — например, в файл somedata.txt (если он не существовал до этого, то будет создан):

$ date > somedata.txt
$ cat somedata.txt 
Чт 18 апр 2024 14:58:52 +04

Иногда для изучения хода программы полезно записывать стандартный вывод и ошибки, возникающие в ходе её выполнения, в файл. Для этого используют файловые дескрипторы, о которых говорилось выше:

Команда

Значение

1

command > 1 > log.txt

Записать в файл стандартный вывод скрипта

2

command > 2 > error_log.txt

Записать в файл сообщения об ошибках

3

command > process.log 2>&1

Записать в один файл стандартный вывод и сообщения об ошибках

4

command &> log.txt

Только для bash: записать в один файл стандартный вывод и сообщения об ошибках

5

command > /dev/null 2>&1

Не выводить стандартный вывод и сообщения об ошибках — отправить их на «нулевое устройство» /dev/null

С оператором &> работает только bash, а другие оболочки — например, sh и ksh — нет. Поэтому, если нужно написать как можно более универсальный скрипт, не рекомендуется использовать этот оператор. Но полезно знать его, чтобы узнавать в чужих скриптах.

У оператора > есть важная особенность: файл, в который записывается стандартный вывод, перезаписывается, то есть его прежнее содержимое удаляется.

Перенаправление вывода с добавлением >>

Оператор >> добавляет стандартный вывод команды в конце текущего содержимого файла, а не заменяет это содержимое. Например, можно перенаправить вывод команды uname -r, которая выводит текущую версию ядра Linux, в созданный файл somedata.txt. Если такого файла нет, он будет создан, как и при работе с оператором >.

$ uname -r >> somedata.txt
$ cat somedata.txt
Чт 18 апр 2024 14:58:52 +06
5.15.0-70-generic

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

Если нужно добавить стандартный вывод или ошибки программы в уже существующий файл, в упомянутых выше командах нужно использовать оператор >>:

./script.sh 2 >> error_log.txt

Следующая команда выгружает массив данных из определённых столбцов в базе данных СУБД MySQL и сохраняет её в файл customers_100.csv:

mysql -u lite -p -e "SELECT id, customer_id, first_name, last_name, city, country FROM customers_global ORDER BY id LIMIT 100" --batch --silent -D customers_db | sed 's/\t/,/g' >> customers_100.csv

1) Команда mysql -u lite -p вызывает клиент командной строки MySQL, указывает имя пользователя и предлагает ему ввести пароль.

2) Часть -e "SELECT id, customer_id, first_name, last_name, city, country FROM customers_global ORDER BY id LIMIT 100" позволяет выполнить SQL-запрос непосредственно из командной строки.

Смысл запроса:

  • оператор SELECT выбирает определенные столбцы — id, customer_id, first_name, last_name, city, country — из таблицы customers_global;
  • оператор ORDER BY id упорядочивает результаты по столбцу id в порядке возрастания;
  • оператор LIMIT 100 ограничивает вывод первыми 100 строками.

3) Ключ -D customers_db указывает, какую базу данных использовать.

4) Опция --batch позволяет выводить данные в формате таблиц, разделённых знаками табуляции — текстовые файлы такого формата проще обрабатывать программными средствами.

Опция --silent отключает отображение имен столбцов — выводятся только строки с данными.

5) Конвейер перенаправляет вывод предыдущей команды команде sed.

6) Команда sed 's/\t/,/g' заменяет все символы табуляции \t на запятые. Таким  образом вывод MySQL преобразуется в формат .csv, то есть comma-separated values — значения, разделённые запятыми.

7) Полученные данные в формате .csv перенаправляются в файл customers_100.csv.

Перенаправление ввода <

Иногда требуется прочитать содержимое файла при помощи определённой программы. Перенаправление ввода позволяет оболочке выполнить команду, используя в качестве источника ввода файл, а не клавиатуру.

Синтаксис в общем виде выглядит так:

команда -опции < файл

Так как команды для обработки файлов принимают их имена в качестве аргументов и без перенаправления, этот оператор чаще всего используется в цепочках из нескольких перенаправлений или конвейеров. При чтении длинных последовательностей команд человеку проще понять, какая программа какой поток принимает, если между ними есть операторы перенаправления.

Например, в одной команде можно использовать перенаправление ввода и вывода:

head -20 < list_100.txt >> list_20.txt

Программа head принимает на вход содержимое файла list_100.txt, в котором 100 строк, и отображает первые 20 из них. Эти 20 строк записываются в файл list_20.txt.

Перенаправление вывода при помощи exec

Если скрипт выполняется долго, удобно переводить его в фоновый режим. Можно сделать это при помощи оператора & (об этом расскажем в следующем разделе Управление заданиями) или запланировать задание для утилиты cron. 

Выполняемые в фоне скрипты не имеют доступа к терминалу для отображения вывода, поэтому обычная практика — сохранять stdout таких скриптов в лог, чтобы при необходимости можно было их отладить. 

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

Когда требуется записать потоки скрипта  stdout и stderror целиком в файл и не выводить на экран, обычно используется команда exec:

exec &> log_file.txt

Эта команда действует в отношении всех следующих строк скрипта, а после его завершения прекращает действовать.

Описанный в разделе выше фрагмент с оператором перенаправления > log_file.txt 2>&1 действует только в отношении той команды, после которой добавлен. Для перенаправления в файл полного вывода скрипта этот фрагмент пришлось бы добавлять к каждой команде в скрипте. Поэтому оператор > обычно используют с отдельными командами в терминале или для записи в файл вывода конкретных команд внутри скрипта.

Следующий скрипт mysql_backup_script.sh запускает резервное копирование базы данных СУБД MySQL и записывает в лог вывод и сообщения об ошибках.

#!/bin/bash

datetime=$(date +"%Y-%m-%d-%H-%M-%S")
db_name="customers_db"
backup_file="/backup/db/${db_name}-${datetime}.sql"
backup_log="/backup/logs/log_${datetime}.log"
backup_options="--routines --triggers --events --single-transaction --quick"
db_username="lite"
export MYSQL_PWD="$SECRET"

mkdir -p /backup/db /backup/logs
exec &>> $backup_log
echo "Запуск задания резервного копирования..."

if mysqldump -u $db_username $backup_options $db_name > $backup_file 2>> $backup_log; then
   echo "Резервное копирование успешно завершено."
else
   echo "Не удалось выполнить резервное копирование. Чтобы узнать причину, проверьте лог: $backup_log"
   exit 1
fi
unset MYSQL_PWD
unset SECRET
echo "Задание завершено $(date)"

Смысл скрипта:

1) Присваиваются значения переменным: 

  • datetime — вывод компонентов команды date (текущей даты): год, месяц, день, часы, минуты, секунды;
  • db_name — имя копируемой базы данных MySQL;
  • backup_file — путь к файлу резервной копии, в который включены значения переменных db_name и datetime;
  • backup_log — путь к файлу лога копирования, в который также включены значения переменных db_name и datetime;
  • backup_options — строка с перечнем параметров копирования. 

Вот значения этих параметров:

  • --routines

В резервную копию (дамп) включаются хранимые процедуры и функции вместе с данными и структурой таблиц.

  • --triggers

Для полного копирования эксплуатационного поведения БД.в дамп включаются триггеры — объекты БД, автоматически выполняющие определённые процедуры при наступлении событий (например, INSERT, UPDATE и DELETE). 

  • --events

В дамп включаются выполняемые по расписанию задания из планировщика событий (Event Scheduler) MySQL .

  • --all-databases

Создается полный дамп экземпляра сервера MySQL, включая созданные пользователями базы данных и системную базу данных MySQL с привилегиями и ролями пользователей.

  • --single-transaction

Создает целостный снимок базы данных в ходе единой транзакции без блокировки таблиц.

  • --quick

Указывает mysqldump получать строки с сервера по одной за раз, а не буферизировать весь набор результатов в памяти. Это повышает производительность и позволяет избежать потенциальных проблем с памятью во время выполнения дампа.

  • Переменной db_username присваивается значение имени пользователя MySQL — lite.
  • Переменной окружения MYSQL_PWD, которую MySQL использует для хранения паролей к базам данных, присваивается значение переменной окружения SECRET. Это значение будет передано в переменную SECRET непосредственно при запуске скрипта, а затем удалено из окружения. Указывать пароли таким способом безопаснее, чем записывать их непосредственно в скрипте, но тоже не очень надёжно. Рекомендуется пользоваться признанными способами аутентификации: например, плагинами LDAP для MySQL.

2) Команда mkdir создаёт каталоги /backup/db и /backup/logs на случай, если они не существуют.

3) Команда exec перенаправляет весь последующий стандартный вывод скрипта и сообщения об ошибках в файл, обозначенный переменной backup_log.

4) Отображается (то есть записывается в лог скрипта) сообщение "Запуск задания резервного копирования..."

5) Условный оператор if проверяет:

  • Если команда mysqldump выполнена успешно и её вывод записан в указанный файл, в лог добавляется сообщение:Резервное копирование успешно завершено.
  • Если команду выполнить не удалось, в лог добавляется сообщение: Не удалось выполнить резервное копирование. Чтобы узнать причину, проверьте лог: <путь к файлу лога>

В этом случае в переменную кода возврата записывается значение 1 — «ошибка».

Код возврата (exit status) — значение, которое дочерний процесс возвращает родительскому в момент завершения. 0 означает успешное завершение процесса, значения от 1 до 255 — завершение с ошибкой. 

6) В лог добавляется сообщение:

Задание завершено <текущая дата>.

7) Значения переменных MYSQL_PWD и SECRET удаляются из окружения.

Скрипт запускается следующей командой:

SECRET="somepassword" ./mysql_backup_script.sh

где somepassword — значение пароля.

Заключение

В этой статье мы рассмотрели несколько видов перенаправлений ввода и вывода в bash. Они позволяют с помощью коротких и ясных команд передавать данные от одной утилиты к другой, ускоряя выполнение операций. В следующей статье расскажем о других способах передачи данных между командами.

Этот материал был полезен?

Скидка 25% новым клиентам!
Закажи сервер сегодня и получи скидку на первый месяц аренды!