Читать книгу 📗 "Операционная система UNIX - Робачевский Андрей Михайлович"
Текст скрипта test4.sh:
#!/bin/sh
tmpfile=/usr/tmp/tmp.$$
...
rm $tempfile
Перенаправление ввода/вывода
Каждая запущенная из командного интерпретатора программа получает три открытых потока ввода/вывода:
□ стандартный ввод
□ стандартный вывод
□ стандартный вывод ошибок
По умолчанию все эти потоки ассоциированы с терминалом. То есть любая программа, не использующая потоки, кроме стандартных, будет ожидать ввода с клавиатуры терминала, весь вывод этой программы, включая сообщения об ошибках, будет происходить на экран терминала. Большое число утилит, с которыми вам предстоит работать, используют только стандартные потоки. Для таких программ shell позволяет независимо перенаправлять потоки ввода/вывода. Например, можно подавить вывод сообщений об ошибках, установить ввод или вывод из файла и даже передать вывод одной программы на ввод другой.
В табл. 1.9 приведен синтаксис перенаправления ввода/вывода, а на рис. 1.9 схематически показаны примеры перенаправления потоков.
Таблица 1.9. Перенаправление потоков ввода/вывода
>file | Перенаправление стандартного потока вывода в файл file |
>>file | Добавление в файл file данных из стандартного потока вывода |
<file | Получение стандартного потока ввода из файла file |
p1 | p2 | Передача стандартного потока вывода программы p1 в поток ввода p2 |
n>file | Переключение потока вывода из файла с дескриптором n в файл file |
n>>file | To же, но записи добавляются в файл file |
n>&m | Слияние потоков с дескрипторами n и m |
<<str | "Ввод здесь": используется стандартный поток ввода до подстроки str. При этом выполняются подстановки метасимволов командного интерпретатора |
<<str | To же, но подстановки не выполняются |

Рис. 1.9. Пример перенаправления стандартных потоков ввода/вывода
Рассмотрим несколько примеров перенаправления потоков.
Запуск некой программы ведения журнала можно выполнить следующим образом:
$ <b>logger >> file.log</b>
При этом вывод программы logger будет записываться в конец файла file.log, сохраняя все предыдущие записи. Если файла file.log не существует, он будет создан. В отличие от этого, использование символа '>' указывает, что сначала следует очистить файл, а затем производить запись.
Стандартным потокам ввода, вывода и вывода ошибок присваиваются дескрипторы — числовые значения, являющиеся указателями на соответствующий поток. Они, соответственно, равны 0, 1 и 2. Перенаправлять потоки можно, используя эти числовые значения. Таким образом, предыдущему примеру эквивалентна следующая запись:
$ <b>logger 1>>file.log</b>
Чаще всего числовое значение дескриптора потока используется для потока ошибок. Например, чтобы подавить вывод ошибок, можно использовать следующую запись:
$ <b>run 2>/dev/null</b>
где /dev/null является псевдоустройством, удаляющим все введенные в него символы.
Командный интерпретатор предоставляет возможность слияния потоков. Например, при запуске команды
$ <b>run_long_program >/dev/null 2>&1 &</b>
сообщения об ошибках будут также выводиться в файл /dev/null. Символ '&' перед именем потока необходим, чтобы отличить его от файла с именем 1. Заметим, что изменение порядка двух перенаправлений потоков приведет к тому, что сообщения об ошибках будут по-прежнему выводиться на экран. Дело в том, что Shell анализирует командную строку слева направо, таким образом сначала будет осуществлено слияние потоков и оба будут указывать на терминал пользователя, а затем стандартный поток вывода (1) будет перенаправлен в файл /dev/null.
Передача потока вывода одной программы в поток ввода другой осуществляется с помощью конвейера '|' (программного канала). Программные каналы часто используются для фильтрации вывода некоторой команды:
$ <b>ps - ef | grep myproc</b>
позволяет получить информацию о конкретном процессе myproc. Утилита ps(1) выводит на экран информацию обо всех процессах в системе, программа grep(1) фильтрует этот поток, оставляя лишь строки, в которых присутствует слово myproc. [14]
Можно усложнить задачу и попытаться получить идентификатор процесса myproc. Однако здесь нам не обойтись без других средств системы. В данном случае мы будем использовать интерпретатор awk(1):
$ <b>ps -ef | grep myproc | awk '{ print $2 }'</b>
Идея заключается в фильтрации второго поля записи о процессе myproc, содержащего идентификатор процесса (см. описание утилиты ps(1)).
Иногда возникает необходимость разместить поток ввода вместе с командой. Для этого используется выражение "ввод здесь". Проиллюстрируем его на примере:
$ <b>at Dec 31 <<!</b>
<b>cat happy.new.year | elm -s"C Новым Годом"</b>
<b>[email protected]</b>
<b>!</b>
По определению, команда at(1) устанавливает вызов команды, полученной ею со стандартного ввода (клавиатуры терминала), на определенное время (в данном случае — на 31 декабря каждого года). С помощью выражения "ввод здесь" мы явно задали вид этой команды, точнее комплекса команд: cat(1) передает текст поздравления программе elm(1), отвечающей за отправление сообщения электронной почты.
Команды, функции и программы
Все команды, которые вводятся в строке приглашения shell, относятся к одной из следующих категорий:
□ встроенные функции
□ функции shell, определенные пользователем
□ внешние программы и утилиты
Непосредственное отношение к shell имеют только первые две категории, а программы и утилиты являются обычными исполняемыми файлами.
Запуск встроенной функции не требует порождения нового процесса, поскольку эта функция реализована в самой программе shell (например, /bin/sh). Соответственно, встроенные функции shell выполняются быстрее всего. Рассмотрим важнейшие встроенные функции shell.
: | Пустая команда. Код возврата всегда 0 (успех). Пустая команда удобна для создания бесконечных циклов, например: while : do ... done |
. runme | Текущий командный интерпретатор выполняет команды, указанные в файле runme. При этом не происходит порождения нового shell, как в случае запуска на выполнение runme. Например, использование в скрипте команды . /usr/bin/include_script |
break [<i>n</i>] | Производит выход из цикла <i>for</i> <i>while</i> <i>n</i> ps -ef | awk '{ print $1 " " $2}' | while read uid pid do if [$pid -eq $PID] then echo pid=$pid user=$uid break fi done |
cd [dir] | Осуществляет переход в каталог <i><b>dir</b></i> |
echo [string] | Строка <i>string</i> |
exec runme | Выполняет программу <i>runme</i> |
exit [<i>n</i>] | Завершает работу текущего интерпретатора (или скрипта) с кодом возврата <i>n</i> |
export [name1], [name2...] | Помещает переменные, указанные в качестве аргументов, в окружение текущего shell, делая их тем самым экспортируемыми, т.е. доступными для запускаемых из интерпретатора программ |
hash [-r] [<i>command</i>, <i>command</i>...] | Для каждой команды, указанной в качестве аргумента, запоминается полный путь. Таким образом, при последующих вызовах этих команд поиск не производится. Ключ -r |
jobs | Если командный интерпретатор поддерживает управление заданиями, данная команда выводит список текущих заданий. См. раздел «Система управления заданиями», далее в этой главе |
kill [-<i>sig</i>] <i>pid1 pid2</i>... | Посылает сигнал, определённый параметром sig, процессам, указанным параметрами <i>pid</i> <i>pid</i> |
pwd | Выводит имя текущего каталога |
read <i>var1 var2</i> ... | Построчно считывает слова (т.е. группы символов, разделённые пробелами) из стандартного потока ввода, последовательно присваивая переменным <i>var</i> |
return [<i>n</i>] | Осуществляет выход из функции с кодом возврата <i>n</i> |
set | При задании без параметров выводит список определённых переменных |
shift [<i>n</i>] | Производит сдвиг позиционных параметров, хранящихся в $1, $2 и т.д. на <i>n</i> |
test | Вычисляет условное выражение. Возвращает значение 0 — истина, или 1 — ложно. См раздел условные выражения далее в этой главе |
times | Выводит суммарное время использования процессора программами, запущенными из текущего командного интерпретатора |
trap <i>command sig1 sig2</i> ... | Определяет команду <i>command</i> <i>sig</i> |
type <i>name</i> | Показывает, как <i>name</i> |
ulimit | Выводит или устанавливает значение пределов, ограничивающих использование задачей системных ресурсов (времени процессора, памяти, дискового пространства). Ограничения будут рассматриваться в главе 2 |
umask <i>nnn</i> | Устанавливает маску прав доступа для вновь создаваемых файлов равной <i>nnn</i> |
unset var1 var2 ... | Удаляет переменные, указанные в качестве аргументов, из списка определенных переменных командного интерпретатора. Некоторые переменные, например PATH, PS1, PS2, не могут быть удалены |
wait <i>pid</i> | Ожидает завершения выполнения процесса с идентификатором <i>pid</i> |