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.

13 комментариев:

  1. спасибо, хороший пост.

    ОтветитьУдалить
  2. Пожалуйста :) Рад, что понравилось :)

    ОтветитьУдалить
  3. Добрый день. а Вы не подскажете, можно ли сделать так, чтобы если при выполнении действия, если ошибки есть, то они записывались во вновь содаваемый файл, а если ошибок нет, то файл не создавался вообще? сейчас do_something.sh > error.log 2>&1 файл содается всегда, если нет ошибок, то он пустой. Заранее спасибо:) Вера.

    ОтветитьУдалить
  4. Честно говоря, не знаю. Если узнаете как, напишите, плз. Мне тоже интересно.

    ОтветитьУдалить
  5. спасибо, пригодилось. Гуглил по "2>&1", пытаясь понять, почему оно не работает :)

    ОтветитьУдалить
  6. спасибо, пригодилось! :)

    ОтветитьУдалить
  7. Спасибо)понятно и полезно!

    ОтветитьУдалить
  8. Анонимный: Просто в любом случае создается файл,а вы напишите условие,например ошибки загружайте в переменную,и проверяйте ее,если переменная не пустая - создать файл и вкинуть туда ошибки,иначе вывести какое-то сообщение и если файлик есть - то удалить его:
    #!/bin/sh
    if [ -n "$err" ]
    then
    echo $err > /else/path/err
    else
    echo "Nothing to del" 2>&1
    rm /else/path/err
    fi

    ОтветитьУдалить
  9. а не проще ли удалить вконце файл, если он пустой?

    ОтветитьУдалить
  10. сам долго не въезжал, после твоего объяснения на "пальцах" сразу стало все пучком, спс

    ОтветитьУдалить
  11. Я бы переформулировал:
    -----------------------
    параметр "2>&1" — означает "перенаправить стандартный поток ошибок (2) в стандартный поток вывода (1).
    -----------------------
    в
    параметр "2>&1" — означает "перенаправить стандартный поток ошибок (2) туда, куда сейчас направлен стандартный поток вывода (1).

    ОтветитьУдалить