Выбрать главу

echo ... 1>&2

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

Интерпретатор предоставляет возможность размещать стандартный входной поток вместе с командой, а не в отдельном файле, так что командный файл может хранить всю информацию в себе самом. Наша справочная программа 411, работающая с каталогом телефонов, могла быть задана так:

$ cat 411

grep "$*" <<End

dial-a-joke      212-976-3838

dial-a-prayer    212-246-4200

dial santa       212-976-3636

dow jones report 212-976-4141

End

$

Программирующие на языке shell называют такую конструкцию "документ здесь", т.е. входной поток находится здесь, а не в каком-нибудь файле. Началом конструкции служит <<; последующее слово (в нашем примере End) является ограничителем входного потока, включающего все строки до той, которая содержит только данное слово. Интерпретатор выполняет замену конструкций $, `...` и \ в "документе здесь", если только часть слова не экранирована кавычками или обратной дробной чертой, — в этом случае весь документ берется без изменений. В конце главы мы рассмотрим еще более интересный пример с конструкцией "документ здесь".

В табл. 3.2 перечислены различные виды переключения ввода-вывода, допускаемые интерпретатором.

> файл Переключение стандартного выходного потока в файл
>> файл Добавление стандартного выходного потока в файл
< файл Получение стандартного выходного потока из файла
p1 | p2 Передача стандартного выходного потока программы p1 в качестве входного потока для программы p2
^ Устарелый синоним |
n> файл Переключение выходного потока из файла с дескриптором n в файл
n>> файл Добавление выходного потока из файла с дескриптором n в файл
n>&m Слияние выходных потоков файлов с дескрипторами n и m
<<s "Документ здесь": берется стандартный входной поток до строки, начинающейся с s; выполняется подстановка для $, `...` и \
<<\s "Документ здесь" без подстановки
<<'s' "Документ здесь" без подстановки

Таблица 3.2: Переключение ввода-вывода интерпретатора

Упражнение 3.14

Сравните версии программы 411: использующую "документ здесь" и первоначальную. Какую легче сопровождать? Какая более подходит в качестве основы общего служебного средства?

3.8 Циклы в shell-программах

Язык shell — действительно язык программирования: в нем есть переменные, циклы, ветвления и т.п. Здесь мы обсудим основные циклы, а структуры управления рассмотрим более подробно в гл. 5.

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

for перем in список_слов

do

 команды

done

Например, для получения эха имен файлов по одному на строке достаточно задать:

$ for i in *

> do

>  echo $i

> done

Вместо i можно применять любую переменную языка shell, но это обозначение традиционно. Заметьте, что значение переменной получается с помощью $i, однако в заголовке цикла переменную указывают как i. Мы задействовали * для выбора всех файлов текущего каталога, но можно использовать и любой другой список аргументов. Обычно нужно сделать что-нибудь более интересное, чем печать имен файлов. Нам часто приходилось сравнивать набор файлов с их предыдущими версиями, например старую версию гл. 2 (хранимую в каталоге old) с текущей:

$ ls ch2. * | 5

ch2.1 ch2.2 ch2.3 ch2.4 ch2.5

ch2.6 ch2.7

$ for i in ch2.*

> do

>  echo $i

>  diff -b old/$i $i

> echo Добавим пустую строку для красоты

> done | pr -h "diff `pwd`/old `pwd` | lpr &

3712   Номер процесса

$

Выходной поток направлен по конвейеру через команды pr и lpr просто для того, чтобы показать, что это возможно: стандартный выходной поток программ, находящихся внутри цикла for, попадает в стандартный выходной поток самой команды for. С помощью флага -h в команде pr мы поместили в выходной поток заголовок с "архитектурными излишествами", используя два вложенных обращения к pwd. Вся последовательность команд запущена асинхронно (&), так что не нужно ждать ее окончания; & применяется ко всякому циклу и конвейеру.

Мы предпочитаем указанный формат для цикла for, но вы можете сократить его. Единственное ограничение заключается в том, что do и done распознаются как ключевые слова, только если они появляются сразу после перевода строки или точки с запятой. В зависимости от размера цикла for иногда лучше помещать все на одной строке:

for i in список; do команды; done

Следует использовать цикл for для обработки составных команд или в тех случаях, когда не подходит встроенная обработка отдельных команд. Но не применяйте его там, где в отдельной команде есть цикл по именам файлов:

# Плохая идея:

for i in $*

do

 chmod +x $i

done

Предпочтительнее сделать так:

chmod +x $*