UNIX — страница 11 из 115

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

Например,

$ ls

выдает список файлов на ваш терминал. Но если задать

$ ls > filelist

то тот же список файлов помещается вместо этого в файл

filelist
. Символ
>
означает, что выходной поток должен быть помещен в указанный далее файл, а не выведен на терминал. Файл будет создан, если он ранее не существовал, или будет заменено содержимое старого. На своем терминале вы ничего не получите. В качестве другого примера можно слить несколько файлов, "перехватив" выходной поток команды
cat
и направив его в файл:

$ cat f1 f2 f3 > temp

Символ

>>
действует подобно
>
, но указывает на необходимость добавить выходной поток к концу файла. Значит, команда

$ cat f1 f2 f3 >> temp

сольет содержимое

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

Аналогично символ

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

$ mail mary joe torn bob < let

Во всех этих примерах наличие пробелов по обе стороны символа

>
или
<
не обязательно, но такое представление традиционно.

Имея возможность переключать выходной поток с помощью

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

$ who > temp

$ sort < temp

Поскольку команда

who
выдает по одной строке на каждого пользователя, работающего в системе, a
wc -l
производит подсчет строк (подавляя вывод числа слов и символов), можно подсчитать число пользователей с помощью команд:

$ who > temp

$ wc -l < temp

и число файлов в текущем каталоге:

$ ls > temp

$ wc -l < temp

хотя в это число войдет и сам файл

temp
. Можно выдать список имен файлов в три столбца, задав

$ ls > temp

$ pr -3 < temp

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

who
и
grep
:

$ who > temp

$ grep mary < temp

Во всех перечисленных выше примерах, как и в случае имен файлов, содержащих образы типа

*
, важно понимать, что символы
<
и
>
обрабатываются самим интерпретатором
shell
, а не отдельной программой. Благодаря этому переключение входного и выходного потоков возможно для любой программы, причем сама программа даже "не подозревает", что происходит что-то необычное.

Изложенное подводит нас к важному выводу. Команда

$ sort < temp

сортирует содержимое файла

temp
так же, как

$ sort temp

но в их действиях есть различие. Поскольку строка

< temp
обрабатывается интерпретатором
shell
, первая команда
sort
не воспринимает файл
temp
как свой аргумент; она просто сортирует собственный стандартный входной поток, который переключен интерпретатором на файл
temp
. В то же время в последнем случае имя temp передается команде sort в качестве аргумента, она читает его и сортирует файл. Команде sort можно передать список файлов:

$ sort temp1 temp2 temp3

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

$ sort

ghi

abc

def

ctl-c

abc

def

ghi

$

В дальнейшем мы покажем, как реализуется этот принцип.

Упражнение 1.5

Объясните, почему команда

$ ls > ls.out

включает

ls.out
в список имен.

Упражнение 1.6

Объясните результат выполнения команды

$ wc temp > temp

Что произойдет, если вы ошибетесь в имени команды, задав

$ woh > temp

Программные каналы

Все примеры, приведенные в конце предыдущего раздела, основаны на одном и том же приеме: выходной поток одной программы передается в качестве входного потока для другой программы через временный файл. Сам временный файл больше не имеет никакого смысла; в самом деле, неудобно использовать такой файл. Это соображение привело к возникновению одной из фундаментальных концепций системы UNIX, идеи программного канала. Программный канал представляет собой средство связи выходного потока одной программы с входным потоком другой без всяких временных файлов; соединение программным каналом двух или более программ называется конвейером.

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

shell
для создания конвейера:

$ who | sort      
Печать отсортированного списка пользователей

$ who | wc -l     
Подсчет числа пользователей

$ ls | wc -l      
Подсчет числа файлов

$ ls | pr -3      
Вывод списка имен файлов в три столбца

$ who | grep mary 
Поиск определенного пользователя

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

pr
,
grep
,
sort
и
wc
используются именно таким способом.

Можно связать конвейером сколь угодно много программ. Например,

$ ls | pr -3 | lpr

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

$ who | grep mary | wc -l

подсчитывает, сколько раз пользователь Мэри входила в систему.

Программы, связанные конвейером, выполняются одновременно, а не последовательно одна за другой. Это означает, что программы в конвейере могут вступать в диалог; ядро выполняет необходимые операции переключения и синхронизации, чтобы такая схема работала. Большинство команд следует определенному образцу, поэтому они хорошо вписываются в конвейер и могут выполняться в нем на любом месте. Обычный вызов команды имеет вид:

команда флаги возможные имена файлов

Если имена файлов не указаны, то команда читает стандартный входной поток, который по умолчанию поступает с терминала (что удобно для экспериментирования), однако возможно его переключение на файл или программный канал. Кроме того, во многих командах выдача идет в стандартный выходной поток, который по умолчанию направлен на терминал, но его также можно переключить на файл или программный канал.

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