Андрей Попов Заметки, переводы, ссылки на ресурсы

Объяснение идиомы 2>&1 в скриптах оболочки


Перевод поста 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.

Содержание