Перенаправление ввода-вывода в bash
Когда я начинал изучать написание сценариев bash, была такая штука, которую я никак не мог понять — перенаправление ввода-вывода. Снова и снова я перечитывал документацию, дословно переписывал оттуда положенные циферки и амперсанды, но никак не мог понять расстановки этих самых магических символов.
Я предполагаю, что вы знакомы с понятиями «файловый дескриптор», «поток ввода (вывода)», «конвейер» и т.д., но просто никак не можете запомнить, как всем этим пользоваться. На самом деле все просто. Смотрите.
Команда перенаправления вывода позволяет все, что записывается в один файловый дескриптор (откуда), записать вместо этого в другой файловый дескриптор (куда):
откуда > кудаЧто может быть на месте откуда:
- ничего: перенаправление из дескриптора 1 (stdout);
- i: перенаправление из дескриптора i;
- &: перенаправляются сразу
stdoutиstderr.
Что может быть на месте куда:
- &j: перенаправление в дескриптор j;
- имя файла: файл открывается в режиме записи, и перенаправление осуществляется в него.
Если используется оператор перенаправления >> (дописать в конец) вместо > (очистить и писать сначала), то куда может быть только именем файла.
Указываемый дескриптор не обязательно должен быть открыт. Номера с 3 по 9 можно использовать как «перевалочные пункты»; открытые файлы получают дескрипторы, начиная с 10.
Перенаправление ввода работает очень похоже:
куда < откудаТеперь при чтении из куда на самом деле чтение будет происходить из откуда. Тут по сравнению с перенаправлением вывода все наоборот. Куда может быть пустым (stdin) или номером дескриптора. Заметьте, что символ & здесь уже не годится — нельзя читать из двух потоков сразу. Откуда может быть или вида &i, или именем файла, который при этом открывается для чтения.
Указанное в команде перенаправление работает только для одной строки сценария; чтобы перенаправление было постоянным, нужно выполнить его с помощью exec. В одной строке можно указывать несколько перенаправлений, причем они комбинируются. Например:
ps -aF > myfile 2>&1
Здесь сначала stdout перенаправляется в файл, а затем stderr перенаправляется в stdout. В результате и stdout, и stderr перенаправляются в файл. Замечу, что перенаправление выполняется по порядку. То есть, такая команда будет работать по-другому:
ps -aF 2>&1 > myfile
Здесь stderr будет выведет на консоль. Правило простое: перенаправление происходит в текущую конечную точку перенаправления дескриптора (если не поняли, прочитайте предложение еще раз).
Еще один тонкий момент: как быть с конвейерами? Распространяется ли действие перенаправления на весь конвейер? Правила просты:
- перенаправление действует только на ту команду в конвейере, где оно задано;
- команда выдает в конвейер данные, выведенные в дескриптор 1 (с учетом перенаправлений);
- команда берет из конвейера данные, полученные из дескриптора 0 (с учетом перенаправлений).
Например, вот так можно перенаправить поток ошибок в конвейер:
ps -aF 3>&1 1>&2 2>&3 | grep something
Здесь stderr и stdout фактически меняются местами.
Конечно, можно много чего еще сказать по поводу перенаправления: про одновременное перенаправление и ввода, и вывода, про перенаправление блока команд, про перенаправление с помощью exec, про закрытие дескрипторов и т.д. Но тогда пост получится слишком объемным, и им неудобно будет пользоваться как справкой. Если у вас есть какие-то вопросы, готов ответить в комментариях. За найденные ошибки также буду признателен.


Спасибо — раньше не встречал столь сжатого и ясного описания. А разбираться лень было :)
Именно поэтому и написал. Знаю, как всем лень :)
Иван, а можно тебя попросить открыть возможность писать комментарии в твоем блоге, подписываясь OpenID?
Открыл. Честно говоря, думал, что оно доступно по-умолчанию…