Перенаправление ввода-вывода
Всё в Linux — это файлы, в том числе — ввод и вывод. Операционная система идентифицирует файлы с использованием дескрипторов. Каждому процессу позволено иметь до девяти открытых дескрипторов файлов. Оболочка bash резервирует первые три дескриптора с идентификаторами 0, 1 и 2. Вот что они означают.
- 0, STDIN — стандартный поток ввода
- 1, STDOUT — стандартный поток вывода
- 2, STDERR — стандартный поток ошибок
STDIN
STDIN
— это стандартный поток ввода оболочки. Для терминала стандартный ввод — это клавиатура. Когда используется символ перенаправления ввода — <
, Linux заменяет дескриптор файла стандартного ввода на тот, который указан в команде. Система читает файл и обрабатывает данные так, будто они введены с клавиатуры.
Многие команды bash принимают ввод из STDIN
, если в командной строке не указан файл, из которого надо брать данные. Например, это справедливо для команды cat
.
Когда вы вводите команду cat
в командной строке, не задавая параметров, она принимает ввод из STDIN
. После того, как вы вводите очередную строку, cat
просто выводит её на экран. И останавливается после того как получает EOF
. EOF
вводится нажатием сочетания клавиш Ctrl+D.
STDOUT
STDOUT
— стандартный поток вывода оболочки. По умолчанию это — экран. Большинство bash-команд выводят данные в STDOUT
, что приводит к их появлению в консоли. Данные можно перенаправить в файл, для этого используется символ перенаправления вывода >
.
Можно осуществить перенаправление вывода в файл с добавлением в конец с помощью >>
. При этом информация, хранящаяся в файле не будет удалена, а вся новая информация будет добавлена в конец этого файла.
STDERR
STDERR
представляет собой стандартный поток ошибок оболочки. По умолчанию этот дескриптор указывает на то же самое, на что указывает STDOUT
, именно поэтому при возникновении ошибки мы видим сообщение на экране.
Рассмотрим простой пример — нужно записать в файл data.txt
содержимое директории data
. Если директория не существует, сообщение об ошибке надо записать в файл error.txt
. Чтобы этого добиться, нужно использовать команды перенаправления для соответствующих дескрипторов с указанием файлов, куда должны попадать ошибки и стандартный вывод:
$ ls data 1> data.txt 2> error.txt
Если надо, STDERR
и STDOUT
можно перенаправить в один и тот же файл, воспользовавшись &>
:
$ ls data &> data-error.txt
Устаревшая форма записи:
$ ls data > data-error.txt 2>&1
Оболочка определяет порядок перенаправления потоков в командной строке слева направо, поэтому приведенный выше пример нельзя записать так:
$ ls data 2>&1 > data-error.txt
В этом случае сначала вывод ошибок перенаправляется в стандартный вывод (на экран), а потом стандартный вывод перенаправляется в файл data-error.txt
. То есть, ошибки будут выведены в консоль, а данные — в файл.
Для дозаписи в файл data-error.txt
$ ls data &>> data-error.txt
Перенаправление вывода в скриптах
Существует два метода перенаправления вывода в сценариях командной строки:
- Временное перенаправление, или перенаправление вывода одной строки
- Постоянное перенаправление, или перенаправление всего вывода сценария
Временное перенаправление вывода
В сценарии можно перенаправить вывод отдельной строки в STDERR
. Чтобы это сделать, достаточно использовать команду перенаправления, указав дескриптор STDERR
, при этом перед номером дескриптора надо поставить символ амперсанда:
#!/bin/bash
echo "This is an error" >&2
echo "This is normal output"
Если запустить этот скрипт, обе строки попадут на экран, так как, как вы уже знаете, по умолчанию ошибки выводятся туда же, куда и обычные данные.
$ ./test.sh This is an error This is normal output
Запустим скрипт так, чтобы вывод STDERR
попадал в файл:
$ ./test.sh 2> error.txt This is normal output $ cat error.txt This is an error
Теперь обычный вывод делается в консоль, а сообщения об ошибках попадают в файл error.txt
.
Постоянное перенаправление вывода
Если в скрипте нужно перенаправлять много выводимых на экран данных, добавлять >&2
к каждому вызову echo
неудобно. Вместо этого можно задать перенаправление вывода в определённый дескриптор на время выполнения скрипта, воспользовавшись командой exec
:
#!/bin/bash
exec 1> output.txt
echo "Это пример перенаправления всего"
echo "вывода сценария в файл output.txt"
echo "без перенаправления каждой строки"
После запуска сценария весь вывод будет перенаправлен в файл output.txt
.
Команду exec
можно использовать не только в начале скрипта, но и в других местах:
#!/bin/bash
# ошибки перенаправляем в файл error.txt
exec 2> error.txt
# строка будет выведена на экран
echo "Первый вывод сценария"
# перенаправляем вывод в файл output.txt
exec 1> output.txt
# строка будет записана в файл output.txt
echo "Второй вывод сценария"
# строка будет записана в файл error.txt
echo "Третий вывод сценария" >&2
Перенаправление ввода в скриптах
Для перенаправления ввода можно воспользоваться тем же способом, который использовался для перенаправления вывода. Например, команда exec
позволяет сделать источником данных для STDIN
какой-нибудь файл:
$ exec 0< data.txt
Эта команда указывает оболочке на то, что источником вводимых данных должен стать файл data.txt
, а не обычный STDIN
. Посмотрим на перенаправление ввода в действии:
#!/bin/bash
exec 0< data.txt
count=1
while read line ; do
echo "Line #$count: $line"
count=$(( $count + 1 ))
done
Подавление вывода
Иногда надо сделать так, чтобы команды в скрипте, который, например, может исполняться как фоновый процесс, ничего не выводили на экран. Для этого можно перенаправить вывод в /dev/null
.
Вот, например, как подавить вывод сообщений об ошибках:
$ ls -al badfile.txt file.txt 2> /dev/null
Тот же подход используется, если, например, надо очистить файл, не удаляя его:
$ cat /dev/null > data.txt