10 мая 2006 г.

2>&1

Я долго пытался понять правила перенаправления потоков вывода в юниксовой оболочке bash. Действительно, вроде всё понятно, но...

Например, было непонятно, почему работает команда, перенаправляющая stderr в stdout:
ls *.txt *.err >file 2>&1

Но при этом не работает вот так:
ls *.txt *.err 2>&1 >file

И уж совсем китайской грамотой казалась строка:
cat file 3>&2 2>&1 1>&3

Да, мне понятно, что эта команда меняет местами потоки вывода stderr и stdout. То, что выводилось в stdout, теперь будет выводиться в stderr и наоборот. Но почему именно так и никак иначе?..

Всё оказалось не просто, а очень просто :) Спасибо замечательной книге "Unix Power Tools" издательства O'Reilly.

Дело в том, что оболочка, при разборе параметров командной строки, читает параметры строго слева направо. Этот факт кажется совсем неудивительным, но оказывается именно это правило оказывает влияние на результат обработки командной строки.

Разберем, как интерпретируется строка "ls *.txt *.err >file 2>&1":
  1. параметр ">file" — означает "перенаправить стандартный поток вывода (stdout) в файл c именем file";

  2. параметр "2>&1" — означает "перенаправить стандартный поток ошибок (2) в стандартный поток вывода (1). Но, так как стандартный поток вывода уже перенаправлен в файл, то и стандартный поток ошибок перенаправляется туда же.
В результате оба потока будут перенаправлены в файл.

А как интерпретируется строка "ls *.txt *.err 2>&1 >file"?
  1. параметр "2>&1" — означает "перенаправить стандартный поток ошибок в стандартный поток вывода". На этот момент поток вывода (1) направляется в терминал, а значит, что поток ошибок тоже перенаправится в терминал.

  2. параметр ">file" — означает "перенаправить стандартный поток вывода (stdout) в файл c именем file".
Как результат — поток вывода будет направлен в файл, а поток ошибок — в терминал.

После этого совсем не трудно понять, как работает команда "cat file 3>&2 2>&1 1>&3". Тут тоже все просто:
  1. поток вывода 3 перенаправить туда же, куда выводится поток вывода 2 — в stderr;

  2. поток вывода 2 перенаправить туда же, куда выводится поток 1 — в stdout;

  3. поток вывода 1 перенаправить туда же, куда выводится поток 3 — в stderr
В результате, потоки вывода stderr и stdout как будто меняются местами — поток 1 идет в stderr, а поток 2 идет в stdout.