Перевод поста Brian Storti “Understanding Shell Script’s idiom: 2>&1”
Объяснение идиомы 2>&1 в скриптах оболочки
Идиома — устойчивое выражение, поговорка или речевой оборот, понятный всем носителям языка. Перевести идиому можно, но понять, о чем идет речь, человеку, который изучает данный язык в качестве иностранного, будет трудно, потому что значение идиомы не вытекает из значения ее слов.
Идиома программирования — устойчивый способ выражения некоторой составной конструкции в одном или нескольких языках программирования. Идиома является шаблоном решения задачи, записи алгоритма или структуры данных путём комбинирования встроенных элементов языка.
Работая с языком программирования или сценариев, мы постоянно используем
некоторые идиомы — конструкции, которые выполняют определенные действия,
решают какую-то распространенную задачу. В скриптах оболочки довольно
распространенной и не всегда хорошо понимаемой идиомой является 2>&1
, как в
примере ls foo > /dev/null 2>&1
Позвольте мне объяснить, что здесь
происходит и почему это работает именно так.
Кратко о перенаправлении ввода/вывода
Перенаправление — это механизм, используемый для отправки вывода команды в
другое место. Например, если мы применяем команду cat
к файлу, то вывод этой
команды по умолчанию будет отображаться на экране:
$ cat foo.txt
foo
bar
baz
Мы можем перенаправить этот вывод в другое место, например, в файл output.txt:
$ cat foo.txt > output.txt
$ cat output.txt
foo
bar
baz
Отметим, что после первого выполнения команды cat
на экран ничего не было
выведено, так как мы изменили размещение стандартного выходного потока
(stdout
) с экрана на файл.
Также важно знать, что есть стандартный поток ошибок (stderr), в который
консольные программы посылают сообщения о возникающих ошибках. Например,
попытаемся применить команду cat
к несуществующему файлу:
$ cat nop.txt > output.txt
cat: nop.txt: No such file or directory
Несмотря на то, что мы перенаправили stdout
в файл, мы по-прежнему видим
сообщения об ошибках на экране. Это правильно, так как мы перенаправили только
выходной поток, а не поток ошибок.
Кратко о дескрипторах файлов
Дескриптор файла — это положительное целое число, которое операционная система назначает открытому файлу. Если у вас открыто 100 файлов, то для них будут созданы 100 дескрипторов.
Замечание. В Unix-системах все является файлом, в том числе стандартные потоки
ввода и ошибок. Поэтому для потоков stdout
и stderr тоже определены файловые
дескрипторы, причем с фисксированными номерами: 1 для sdtout и 2 для stderr.
Собираем части вместе
Вернемся к нашему первому примеру с перенаправлением cat foo.txt
в файл
output.txt. Теперь мы можем переписать эту команду так:
$ cat foo.txt 1> output.txt
Здесь число 1 означает файловый дескриптор для stdout
.
Аналогичным образом, чтобы перенаправить поток ошибок stderr
, нужно перед
оператором перенаправления >
указать файловый дескриптор с номером 2:
# Использование файлового дескриптора (2) потока stderr для перенаправления сообщений об ошибках в файл
$ cat nop.txt 2> error.txt
$ cat error.txt
cat: error.txt: No such file or directory
Теперь вы уже, наверное, догадываетесь, что делает идиома 2>&1
?
Запись &1
означает значение файлового дескриптора 1 (stdout
). Поэтому
выражение 2>&1
означает, что stderr
(2) перенаправляется в то же место,
куда был перенаправлен stdout
(1):
$ cat foo.txt > output.txt 2>&1
$ cat output.txt
foo
bar
baz
$ cat nop.xt > output.txt 2>&1
$ cat output.txt
cat: nop.txt: No such file or directory
Резюме
- Консольные программы посылают вывод в два места: стандартный выходной поток
(
stdout
) и стандартный поток ошибок (stderr
). - Вывод в стандартные потоки можно перенаправить в другое место (например, в файл).
- Потоку
stdout
соответсвует файловый дескриптор 1, потокуstderr
— файловый дескриптор 2. - Запись
command > output
является сокращением дляcommand 1> output
. - Получить значение файлового дескриптора можно с помощью конструкции
&[FILE_DESCRIPTOR]
. - Запись
2>&1
перенаправитstderr
туда же, куда направленstdout
.