
- •Список сокращений
- •Введение
- •Автоматизация работы в командной строке
- •Применение автоматизации
- •Особенности командных оболочек
- •Основы разработки скриптов
- •Создание простого скрипта
- •Переменные
- •Работа с потоками ввода-вывода
- •Расширенные возможности разработки скриптов
- •Массивы
- •Математические операции
- •Условный оператор if
- •Оператор выбора case
- •Функции
- •Система контроля версий git
- •Упражнения
- •Индивидуальные задания
- •Контрольные вопросы
- •Список литературы
Работа с потоками ввода-вывода
Для каждой команды, записанной в файле скрипта, возможно перенаправление ее потоков ввода-вывода точно так же, как и при вводе отдельных команд в окне терминала. Однако зачастую при разработке скрипта требуются более расширенные возможности, которые предоставляет командная оболочка.
При запуске скрипта, ОС создает соответствующий ему процесс и автоматически связывает с порожденным процессом 9 файловых дескрипторов, обращение к которым внутри скрипта может осуществляться по их порядковым номерам от 0 до 9.
Файловому дескриптору с номером 0 соответствует стандартный поток ввода stdin [ CITATION Sta \l 1049 ], который используется для ввода данных в процесс с клавиатуры или для получения данных из файла. Файловому дескриптору с номером 1 соответствует стандартный поток stdout [ CITATION Sta1 \l 1049 ], данные которого отображаются на экране терминала. Файловому дескриптору с номером 2 соответствует стандартный поток вывода ошибок stderr [ CITATION Sta2 \l 1033 ], который по умолчанию также выводит данные на экран терминала. Файловые дескрипторы от 3 до 9 могут использоваться произвольно.
В Таблице Таблица 1 представлена справка по частым сценариям использования механизмов перенаправления потоков ввода-вывода.
Таблица 1. Часто используемые механизмы перенаправления потоков ввода-вывода
Команда |
Пример |
Описание |
program > %filename% program 1> %filename% |
echo “Hello!” > testfile echo “Hello!” 1> testfile |
перенаправление потока stdout в файл с именем %filename%. Если файла не существовало, то он создается. Если файл существовал и в нем были данные – он очищается перед записью |
program >> %filename% program 1>> %filename% |
echo “First line” >> testfile echo “Second line” 1>> testfile |
перенаправление потока stdout в файл с именем %filename%. Данные в файл дописываются в конец |
program 2> %filename% |
whoami 2> debugfile |
перенаправление потока stderr в файл с именем %filename%. Если файла не существовало, то он создается. Если файл существовал и в нем были данные – он очищается перед записью |
program 2>> %filename% |
pwd 2>> debugfile whoami 2>> debugfile |
перенаправление потока stderr в файл с именем %filename%. Данные в файл дописываются в конец |
program &> %filename% |
ps &> testfile |
перенаправление потоков stdout и stderr в файл с именем %filename% |
program i>&j |
script.sh 4>&5 |
перенаправление файла с дескриптором i в дескриптор j. |
program < %filename% program 0< %filename% |
grep word < file_of_words grep word 0< file_of_words |
ввод в программу данных из файла с именем %filename% |
program < %inputfile% > %outputfile% |
grep word < file_of_words > outputfile |
ввод в программу данных из файла с именем %inputfile% и перенаправление потока stdout в файл %outputfile% |
Для того, чтобы управлять перенаправлением дескрипторов или задействовать дескрипторы с номерами от 3 до 9 в скрипте часто используется встроенная команда «exec» [ CITATION exe \l 1033 ]:
|
exec 3 <> filename # дескриптор 3 может использоваться для работы с filename exec 0< filename # stdin скрипта будет браться из filename, вместо клавиатуры exec 4> filename # отправлять данные из дескриптора 4 в файл filename exec 5>&2 # перенаправлять дескриптор 5 в дескриптор 2 (stderr) exec 5>&- # закрыть дескриптор 5 |
|
Помимо перенаправления дескрипторов с помощью exec можно замещать текущий процесс новым процессом.
Чтение данных из одного из дескрипторов или произвольного файла может быть произведено в скрипте с помощью встроенной команды «read»:
|
read [-ers] [-u fd] [-t timeout] [-p <PROMPT>] [-a <ARRAY>] [-n <NCHARS>] [-d <DELIM>] [<NAME...>] |
|
По умолчанию она считывает одну строку данных из стандартного потока ввода (stdin) и записывает ее в переменную с именем <NAME>. Например, при вызове двух команд
|
read A echo $A |
|
скрипт сначала будет ожидать ввода данных с клавиатуры, затем эти данные окажутся в переменной A, а после выведены на экран терминала с помощью echo. Если при вызове read указать несколько переменных, то в каждую из них будет помещено одно слово из введенной пользователем строки (слова разделяются пробелами или табуляцией).
С помощью опции «-n» возможно указать для read какое количество байт необходимо считать из stdin:
|
read -n 4 A # ожидание получения из stdin 4 байт для их помещения в A |
|
Как только read получит указанное количество байт, то они будут помещены в переменную и выполнение скрипта продолжится.
Для того, чтобы считать данные из произвольного файлового дескриптора, используется опция «-u». Для чтения данных из произвольного файла используется перенаправление стандартного потока ввода:
|
exec 6<>filename read -u 6 -n 2 A # получение двух байт в переменную A из дескриптора 6 read -n 2 A < filename # получение двух байт в переменную A из файла filename |
|
Остальные опции read дают следующие возможности:
«-a» считывает по слову в массив <ARRAY> вместо переменных (подробнее про массивы см. п. 3.1);
«-d» распознает <DELIM> вместо символа <newline> для обозначения конца строки;
«-e» для интерактивной оболочки: использует bash-интерфейс readline для того, чтобы читать данные;
«-p» добавляет строку приглашения <PROMPT>. Обычно в ней выводят подсказку, перед тем как команда read будет считывать данные;
«-r» – сырой ввод: отключает интерпретацию специальных символов в считываемых данных;
«-s» – секретный ввод: не выводит в терминал введенные данные (для паролей);
«-t» – таймаут, ожидает данных <TIMEOUT> секунд и потом выходит.
В Листинге Листинг 5 приведен пример скрипта, который демонстрирует возможности работы с файловыми дескрипторами с помощью чтения данных из файла /proc/cpuinfo, в котором содержится информация об используемых процессорах. Пример его содержимого показан на Рисунке Рисунок 5.
Рисунок 5. Содержимое файла /proc/info
Листинг 5. Пример работы с файловыми дескрипторами
-
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
#!/bin/bash
exec 3</proc/cpuinfo
read -u 3 processor delim1 delim2 pr_value
read -u 3 model delim1 delim2 model_value
read -p "Entry name of file for model:" filename
echo "processor $pr_value - $model_value" > $filename
echo "Model of processor $pr_value is $model_value"
exec 3>&-
В строке 3 с помощью exec файловый дескриптор 3 связывается с файлом /proc/cpuinfo. Строки 5 и 6 содержат вызов read, который помещает слова из файлового дескриптора 3 в соответствующие переменные. В строке 8 у пользователя запрашивается имя файла filename. В строках 10 и 11 с помощью echo записываются строки в файл filename и выводятся в стандартный поток вывода. После этого файловый дескриптор 3 закрывается с помощью exec.