<< Предыдущий раздел | /\ Содержание | >> Следующий раздел

Перенаправление вывода команд в файл

Представим себе ситуацию: хочется посмотреть листинг директории /bin (в которой лежат многие программы). Выполняем команду "ls -l /bin" и... Результат в экран не помещается. А ведь бывают директории еще большего объема. Что делать?

Большинство команд выводят информацию на терминал, а некоторые (например, текстовый редактор) вводят данные с терминала. В Unix есть почти универсальное правило: терминал может быть заменен для ввода, вывода или и того, и другого, на файл.

Так, чтобы сохранить вывод нашей команды в файл, надо написать:

bobby:~% ls -l /bin >filelist         
bobby:~% 

При этом весь вывод команды ls будет вместо терминала отправлен в файл. Символ ">" означает, что выходной поток должен быть помещен в указанный далее файл, а не выведен на терминал. Если в файле что-то было, то старое содержимое будет стерто.

Получив таким образом список файлов, его можно просмотреть командой less.

Еще один пример: можно слить содержимое нескольких файлов в один файл, "перехватив" выходной поток команды cat и отправив его в файл:

bobby:~% cat file1 file2 file3 >all    
bobby:~% _

Сделать то же самое при помощи команды cp нельзя:

bobby:~% cp file1 file2 file3 all
cp: copying multiple files, but last argument (all) is not a directory 
Try `cp --help' for more information.
bobby:~% _

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

bobby:~% cat file1 >all
bobby:~% cat file2 file3 >>all         
bobby:~% _

Первая команда cat скопирует содержимое file1 в all, а вторая -- допишет к нему содержимое file2 и file3.

Использование ">>" очень удобно, если есть некий "долгоживущий" файл (например, протокол каких-то действий или результатов), к которому иногда надо дописывать данные в конец. Просто указать ">>" с какой-нибудь командой обычно намного быстрее, чем загружать файл в текстовый редактор и что-то дописывать.

Конвейеры

Теперь вернемся к нашему первому примеру: как посмотреть листинг большой директории. Мы отправили вывод команды ls в файл, а потом запустили less для его просмотра. Сам же временный файл не имеет никакого смысла -- потом он больше не нужен.

Можно обойтись без временного файла, воспользовавшись конвейером:

bobby:~% ls -l /bin | less             

Символ "|" означает, что надо взять выходной поток от первой команды, и отправить его на входной поток второй.

Большинство команд, выдающих информацию из файла на экран (cat, more, less) устроены так, что они будут читать входной поток, если не указан никакой файл -- вот почему less показывает то, что ему "пришло" от ls.

Замечание
В отличие от Dos, где тоже поддерживаются операторы перенаправления ввода/вывода, в Unix команды в конвейере запускаются одновременно, и данные передаются через программный канал (это специальное средство, предоставляемое ядром Unix) сразу от одной программы к другой, а не через скрытый временный файл.

Можно заставить команду читать вместо терминала не только выходной поток другой команды, но и обычный файл. Это делается при помощи оператора "<", который указывает, что вместо терминала надо брать входной поток из указанного далее файла. Пример (команда "sort" построчно сортирует входные данные):

bobby:~% cat proverbs
What is worth doing, is worth doing well.      
Art is long, life is short.
History repeats itself.
Dead men tell no tales.
_______________________
bobby:~% sort <proverbs 
Art is long, life is short.
Dead men tell no tales.
History repeats itself.
What is worth doing, is worth doing well.
_______________________
bobby:~% _

Поток сообщений об ошибках

Что будет, если мы попросим ls показать файлы, которых нет?

bobby:~% ls -l file1 file2 file10 file11                   
/bin/ls: file10: No such file or directory
/bin/ls: file11: No such file or directory
-rw-r--r--   1 ivanov   lab5           76 Feb 21 13:23 file1
-rw-r--r--   1 ivanov   lab5           32 Feb 21 13:23 file2
bobby:~% _

Файлы которые есть, ls покажет, а про остальные скажет, что их нет. А теперь перенаправим вывод ls в файл:

bobby:~% ls -l file1 file2 file10 file11 >filelist         
/bin/ls: file10: No such file or directory
/bin/ls: file11: No such file or directory
bobby:~% _

В чем же дело?! Казалось бы, на экране ничего не должно появиться...

А дело в том, что кроме выходного потока есть еще поток сообщений об ошибках, и "культурно" написанные программы все "ругательства" отправляют туда. Чтобы перенаправить в файл поток сообщений об ошибках (обычно его сокращенно называют stderr), надо перед символом ">" указать номер потока -- 2. Пример:

bobby:~% ls -l file1 file2 file10 file11 >filelist 2>errs  
bobby:~% cat filelist
-rw-r--r--   1 ivanov   lab5           76 Feb 21 13:23 file1
-rw-r--r--   1 ivanov   lab5           32 Feb 21 13:23 file2
bobby:~% cat errs
/bin/ls: file10: No such file or directory
/bin/ls: file11: No such file or directory
bobby:~% _

Замечание
Такое перенаправление потока stderr невозможно в оболочках csh и tcsh -- в них для этого используется другой, более запутанный синтаксис.

Замечание
Те, кто программировал на языке C, знают, что там есть три стандартных файловых потока для ввода/вывода: stdin (входной поток, дескриптор 0), stdout (выходной поток, дескриптор 1) и stderr (поток сообщений об ошибках, дескриптор 2). Операторы ">", "<" и "2>" перенаправляют именно эти потоки.

С потоком stderr обычно требуется сделать одну из трех вещей: перенаправить его туда же, куда и выходной поток, направить в отдельный файл, или отправить "в никуда". В первом случае можно воспользоваться специальной формой оператора перенаправления вывода -- "2>&1" ("отправь второй поток (stderr) туда же, куда и первый (stdout)):

bobby:~% ls -l file1 file2 file10 file11 >filelist 2>&1    
bobby:~% cat filelist
/bin/ls: file10: No such file or directory
/bin/ls: file11: No such file or directory
-rw-r--r--   1 ivanov   lab5           76 Feb 21 13:23 file1  
-rw-r--r--   1 ivanov   lab5           32 Feb 21 13:23 file2
bobby:~% _

Этот способ работает и тогда, когда стандартный вывод отправляется по конвейеру другой программе.

Во третьем же случае надо отправить stderr в "черную дыру" -- файл /dev/null:

bobby:~% ls -l file1 file2 file10 file11 >filelist 2>/dev/null  
bobby:~% cat filelist
-rw-r--r--   1 ivanov   lab5           76 Feb 21 13:23 file1
-rw-r--r--   1 ivanov   lab5           32 Feb 21 13:23 file2
bobby:~% _

Какие еще есть операторы перенаправления ввода/вывода?

Подробную информацию на эту тему можно найти в info-документации на zsh, набрав команду "info zsh redirection".

Здесь же следует лишь заметить, что zsh обладает способностью перенаправлять вывод в несколько файлов сразу. Это бывает полезно, если хочется запустить какую-нибудь "долгоиграющую" команду так, чтобы ее вывод сохранялся в файле, но при этом отображался бы и на экране -- для этого надо перенаправить вывод одновременно в файл "протокола" и в файл /dev/tty.


<< Предыдущий раздел | /\ Содержание | >> Следующий раздел