Компания ALT Linux
Опубликован: 14.12.2004 | Доступ: свободный | Студентов: 12289 / 1476 | Оценка: 4.19 / 3.84 | Длительность: 18:18:00
ISBN: 978-5-9556-0019-1
Лекция 11:

Shell как язык программирования и интегратор

< Лекция 10 || Лекция 11: 12345 || Лекция 12 >

Ввод/вывод

Операции ввода-вывода и работа с файлами, строго говоря, не вытекают из требований алгоритмической полноты, однако любой не совсем уж теоретический язык программирования обязательно их имеет для связи с реальным миром, т. е. с пользователем и системой. Есть все это и в shell. С операцией вывода мы уже знакомы - это echo. Операция ввода имеет формат read список_переменных; она читает со стандартного ввода строку, делит ее на слова по правилам разбора командной строки и помещает первое слово в первую переменную, второе - во вторую и т. д. В последнюю переменную попадает остаток строки, независимо от количества слов в нем. Если переменных, наоборот, больше, чем слов, стоящие в конце остаются пустыми.

Работа с файлами устроена с наименьшим расходом символов. Для того чтобы перенаправить вывод команды в файл, достаточно написать после нее > файл, при этом старое содержимое файла (если он был) пропадет. Операция команда >> файл дописывает вывод команды в конец файла. Для того чтобы команда вводила из файла, а не с терминала, ее надо запустить так: команда < файл. Например, cat < fileIn > fileOut ничего не будет ни вводить с клавиатуры, ни выводить на экран - ничего, кроме диагностических сообщений вроде cannot open fileIn: No such file or directory.

Дело в том, что сообщения об ошибках направляются особым путем, на так называемый стандартный вывод ошибок (дескриптор 2, stderr ). Изначально и стандартный вывод (дескриптор 1, stdout ), и стандартный вывод ошибок направлены в терминал, а операция > перенаправляет именно стандартный вывод. Это сделано для того, чтобы штатные сообщения, которые, возможно, какая-нибудь другая программа будет автоматически обрабатывать, не смешивались с нештатными, которые могут быть весьма разнообразными и зависят от вида и версии shell, от системы и т. п. Ошибки можно записать в другой файл, написав команда 2> файл_с_ошибками. Если необходимо направить диагностические сообщения туда же, куда и стандартные, используйте явное указание номера дескриптора в правой части " > ": cat file1 > file2 2>&1.

Крайняя простота перенаправления ввода/вывода в shell делает удобной связь между программами посредством файлов с данными. Но чаще обмен данными организуют напрямую: вывод одной программы направляется на вход другой. В UNIX есть средство, именуемое каналом (pipe, он же fifo), позволяющее удобно и прозрачно для самих процессов это организовать. В shell каналом пользуется уже известная нам конструкция команда1 | команда2 (например, ls -l | less, направляет выдачу ls на вход less, которая показывает ее постранично). Работает это очень быстро (через буфер обмена в памяти системы), записывается просто, поэтому из команд, связанных по вводу/выводу, можно строить целые конвейеры:

$ ls -1 /bin | grep o | sort | tail -3
domainname
echo
hostname

Простота перенаправления ввода/вывода имеет свои корни. На самом деле в UNIX работа программ с файлами, с терминалом и другими устройствами, с каналом и т. п. организована одинаково, поэтому процесс, мирно выводящий текст на стандартный вывод, может и не подозревать, что это - файл, или это - канал, и с другой его стороны все читает какой-то процесс. Между прочим, в конвейер можно встроить команду | tee файл |, которая раздвоит поток данных и направит одну ветку в файл ( tee - английское название буквы "T", отражающей суть работы этой утилиты).

Сгруппировать потоки ввода/вывода нескольких команд (например, перенаправить весь вывод в один файл) можно, окружив их скобками - круглыми или фигурными. Для выполнения команд в круглых скобках запустится отдельная копия командного интерпретатора, а фигурные только дают указание текущему shell группировать ввод/вывод; это, так сказать, дополнительные операторные скобки, которые можно использовать, если shell не предоставил основных (таких, как while ... do ... done ).

Мощный механизм связывания - подстановка вывода команды. Обычно подстановка вывода происходит, если заключить команду в обратные апострофы " ` ", этот символ еще зовется гравис ; во многих shell сработает и конструкция $(команда). Подстановка вывода происходит так: сначала выполняется команда, а все, что она выдает на стандартный вывод, передается shell в виде строки:

$ echo `ls -1s`
total 64 56 fhs-2.2.ps.gz 4 myscript 4 o

Заметим, что, хотя ls -1 выдает список файлов в столбик, shell, передавая параметры команде echo, считает переводы строки такими же разделителями, как пробелы или символы табуляции, и echo уже не видит их.

Работа с процессами

В языке программирования общего назначения управление задачами обычно перекладывается на систему, и работают с ними при помощи какой-нибудь системной библиотеки. Но раз уж shell - оболочка, то она-то как раз должна уметь управлять процессами как можно проще. В UNIX нет единого системного вызова "запустить программу как новый процесс", потому что это действие с точки зрения системы не атомарно. Для запуска нового процесса следует, с одной стороны, зарегистрировать еще один процесс в таблице процессов системы, а с другой - запустить новую программу. Нередко новый процесс регистрировать не надо, потому что одна программа запускает другую вместо себя (попросту передает ей управление и исчезает). Для этого в системе предусмотрены вызовы семейства exec(). В shell тоже есть команда exec, запускающая процесс вместо текущей копии командного интерпретатора.

В программе (например, на Си) первую половину неатомарной операции "запустить программу как новый процесс" выполняет системный вызов fork() ("вилка", "развилка"). В результате вызова fork появляется два совершенно одинаковых процесса ( окружение, память и даже файловые дескрипторы родительского процесса копируются дочернему), которые отличаются только PID, а еще тем, что дочернему процессу fork возвращает ноль, а родительскому - PID дочернего. Разобравшись в родственных отношениях, процессы могут спокойно заниматься своими делами.

Чаще всего порожденный процесс немедленно выполняет exec, чем, собственно, и завершается упомянутая неатомарная операция. Обычный запуск команды в shell устроен именно так, причем родительский процесс дожидается завершения работы дочернего с помощью системного вызова wait(). Для того чтобы в shell запустить фоновый процесс, а самому продолжить работу, нужно всего лишь не вызывать wait, что и происходит, когда в конце командной строки стоит &:

$ ( sleep 5; echo "Hi there!" ) &
$ # прошло пять секунд
$ Hi there!
[1]  Done    (sleep 5; echo Hi there!)

Заметим, что "Hi there!" вывелось прямо поверх командной строки, в которой мы могли в это время набирать другую команду. Получившаяся мешанина кого угодно собьет с толку, поэтому вывод фоновых процессов рекомендуется перенаправлять. Что касается ввода, то его перенаправлять обязательно: фоновый процесс не имеет права читать с терминала. PID последней запущенной в фоне команды хранится в переменной !.

< Лекция 10 || Лекция 11: 12345 || Лекция 12 >
Andranik Avakian
Andranik Avakian

41. УК РФ и Комментарии (ст. 273)

М. 2000 г. Издательство: ALT Linux, Институт Логики

Уголовный Кодекс РФ и комментарии к нему?

По ссылке открывается сайт документации Linux, раздел Linux Installation and Getting Started

Сергей Петровский
Сергей Петровский

У Вас написано:

ls -dt1 `grep -il отчет *` | head -1

если знания по шелу мне не изменяют, то должно быть:

ls -dt | `grep -il отчет *` | head -1