Стыкование команд в системе Linux

Стандартный ввод и стандартный вывод

Многие команды системы Linux имеют так называемые стандартный ввод (standard input) и стандартный вывод (standard output), часто сокращаемые до stdin и stdout. Ввод и вывод здесь — это входная и выходная информация для данной команды. Программная оболочка делает так, что стандартным вводом является клавиатура, а стандартным выводом — экран монитора.

Приведём пример с использованием команды cat. Обычно команда cat читает данные из всех файлов, которые указаны в командной строке, и посылает эту информацию непосредственно в стандартный вывод (stdout). Следовательно, команда

/home/larry/papers# cat history-final masters-thesis

выведет на экран сначала содержимое файла history-final, а затем — файла masters-thesis.

Однако если имя файла не указано, программа cat читает входные данные из stdin и возвращает их в stdout. Приведём пример:

/home/larry/papers# cat 
Hello there. 
Hello there. 
Bye. 
Bye.
Ctrl-D/home/larry/papers#

Каждую строчку, вводимую с клавиатуры, программа cat немедленно возвращает на экран. При вводе информации со стандартного ввода конец текста сигнализируется вводом специальной комбинации клавиш, как правило Ctrl-D. Сокращённое название сигнала конца текста — EOT (end of text).

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

/home/larry/papers# sort 
bananas 
carrots 
apples 
Ctrl-Dapples
bananas
carrots /home/larry/papers#

Как видно, строки упорядочены в алфавитном порядке.

Перенаправление ввода и вывода

Допустим, вы хотите направить вывод команды sort в некоторый файл, чтобы сохранить упорядоченный по алфавиту список на диске. Командная оболочка позволяет перенаправить (redirect) стандартный вывод команды в файл, используя символ >. Приведём пример:

/home/larry/papers# sort > shopping-list 
bananas 
carrots
apples
Ctrl-D/home/larry/papers#

Можно увидеть, что результат работы команды sort не выводится на экран, однако он сохраняется в файле с именем shopping-list. Выведем на экран содержимое этого файла:

/home/larry/papers# cat shopping-list 
apples 
bananas 
carrots
/home/larry/papers#

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

/home/larry/papers# sort items shopping-list
/home/larry/papers# cat shopping-list 
apples 
bananas 
carrots
/home/larry/papers#

Однако, можно поступить иначе. Можно перенаправить не только стандартный вывод, но и стандартный ввод, используя символ <:

/home/larry/papers# sort < items 
apples 
bananas 
carrots
/home/larry/papers#

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

Введём понятие фильтра (filter). Фильтром является программа, которая читает данные из стандартного ввода, некоторым образом их обрабатывает и результат направляет на стандартный вывод. Когда применяется перенаправление, в качестве стандартного ввода и вывода могут выступать файлы. Как указывалось выше, по умолчанию, stdin и stdout относятся к клавиатуре и к экрану соответственно. Программа sort является простым фильтром — она сортирует входные данные и посылает результат на стандартный вывод. Совсем простым фильтром является программа cat — она ничего не делает с входными данными, а просто пересылает их на выход.

Использование состыкованных команд

Выше уже демонстрировалось, как использовать программу sort в качестве фильтра. В этих примерах предполагалось, что исходные данные находятся в некотором файле или что эти исходные данные будут введены с клавиатуры (стандартного ввода). Однако как поступить, если вы хотите отсортировать данные, которые являются результатом работы какой-либо другой команды, например, ls?

Будем сортировать данные в обратном алфавитном порядке; это делается опцией -r команды sort. Если вы хотите перечислить файлы в текущем каталоге в обратном алфавитном порядке, один из способов сделать это будет таким. Применим сначала команду ls:

/home/larry/papers# ls 
english-list 
history-final 
masters-thesis
notes 
/home/larry/papers#

Теперь перенаправляем выход команды ls в файл с именем file-list

/home/larry/papers# ls > file-list 
/home/larry/papers# sort -r file-list 
notes 
masters-thesis 
history-final 
english-list
/home/larry/papers#

Здесь выход команды ls сохранен в файле, а после этого этот файл был обработан командой sort -r. Однако этот путь является неизящным и требует использования временного файла для хранения выходных данных программы ls.

Решением в данной ситуации может служить создание состыкованных команд (pipelines). Стыковку осуществляет командная оболочка, которая stdout первой команды направляет на stdin второй команды. В данном случае мы хотим направить stdout команды ls на stdin команды sort. Для стыковки используется символ |, как это показано в следующем примере:

/home/larry/papers# ls | sort -r 
notes 
masters-thesis
history-final 
english-list 
/home/larry/papers#

Эта команда короче, чем совокупность команд, и её проще набирать.

Рассмотрим другой полезный пример. Команда

/home/larry/papers# ls /usr/bin

выдаёт длинный список файлов. Большая часть этого списка пролетает по экрану слишком быстро, чтобы содержимое этого списка можно было прочитать. Попробуем использовать команду more для того, чтобы выводить этот список частями:

/home/larry/papers# ls /usr/bin | more

Теперь можно этот список "перелистывать".

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

/home/larry/papers# ls | sort -r | head -1 notes
/home/larry/papers\#

где команда head -1 выводит на экран первую строку получаемого ей входного потока строк (в нашем случае поток состоит из данных от команды ls), отсортированных в обратном алфавитном порядке.

Недеструктивное перенаправление вывода

Эффект от использования символа > для перенаправления вывода файла является деструктивным; иными словами, команда

/home/larry/papers# ls > file-list

уничтожит содержимое файла file-list, если этот файл ранее существовал, и создаст на его месте новый файл. Если вместо этого перенаправление будет сделано с помощью символов >>, то вывод будет приписан в конец указанного файла, при этом исходное содержимое файла не будет уничтожено. Например, команда

/home/larry/papers# ls >> file-list

приписывает вывод команды ls в конец файла file-list.

Следует иметь в виду, что перенаправление ввода и вывода и стыкование команд осуществляется командными оболочками, которые поддерживают использование символов >, >> и |. Сами команды не способны воспринимать и интерпретировать эти символы.