Документация по ОС FreeBSD
Воскресенье, 09.11.2025, 08:36
Главная
Регистрация
Вход
Приветствую Вас
Гость
|
RSS
Меню сайта
Главная страница
Новости в мире Unix
NEW
Каталог файлов
NEW
Установка и настройка
Ports & Packages
cvs
Безопасность
Работа с железом
X Window
Multimedia
Man pages
Net
Apache
DNS
FTP
Mail
Samba
Squid
SSH
VPN
РРР
Shell
IPFW
Tips'n'tricks
RFC
Книги по FreeBSD
Темы экзамена BSDA
Гостевая книга
Форум на bsdportal.ru
Каталог сайтов
Самый свежий софт
Каталог ссылок
Категории каталога
Shell
[40]
Главная
»
Статьи
»
Программирование
»
Shell
Перенаправления в bash при помощи exec [2010]
Если вы частенько работаете в командной строке, вы должны быть знакомы с перенаправлением ввода/вывода. Но, возможно, вы не знакомы с перенаправлением ввода/вывода внутри bash-скрипта. Я не имею ввиду перенаправление, используемое при вызове других программ из скрипта, я говорю о перенаправлении ввода/вывода вашего скрипта в целом, начиная с момента его запуска.
Давайте, например, предположим, что вам понадобилось снабдить ваш скрипт опцией «--log», при помощи которой пользователь мог бы перенаправлять весь вывод работы вашего скрипта в нужный лог-файл. Конечно, пользователь мог бы просто перенаправить вывод скрипта средствами bash, но давайте представим, что по каким-то причинам такое решение не годится. Давайте реализуем это:
#!/bin/bash
echo hello
# Разбираем опции командной строки.
# Выполняем следующий код, если нашли опцию --log
if test -t 1; then
# Stdout привязан к терминалу
exec >log
else
# Stdout не привязан к терминалу,
# протокол вести не получится
false
fi
echo goodbye
echo error >&2
В первом утверждении if при помощи test выполняется проверка, подключён ли файловый дескриптор с номером один (поток стандартного вывода) к терминалу. Если это так, тоexec «переоткрывает» его для записи в файл log. Вызов exec в контексте текущей оболочки без передачи ей команды, а только с указанием перенаправления вывода, приводит к тому, что вы можете открывать/закрывать файлы, дублируя их дескрипторы. Если же файловый дескриптор с номером 1 не подключён к терминалу, то мы просто не делаем ничего.
Если вы запустите вышеприведённый скрипт, то вы увидите, что первая и последняя echoвыполнят свой вывод в терминал. Первая echo сработает потому, что она появляется до включения перенаправления, а вторая — поскольку в сценарии её вывод перенаправлен в стандартный поток ошибок (файловый дескриптор номер 2). И как же перенаправить стандартный поток ошибок в тот же файл? Всего ли одно небольшое изменение в вызовеexec:
#!/bin/bash
echo hello
if test -t 1; then
# Stdout привязан к терминалу
exec >log 2>&1
else
# Stdout не привязан к терминалу,
# протокол вести не получится
false
fi
echo goodbye
echo error >&2
В примере выше exec перенаправляет поток ошибок туда же, куда направлен поток вывода (это, собственно, и называется дублированием файловых дескрипторов). Имейте ввиду, что порядок здесь очень важен: если вы его измените и переоткроете сперва поток ошибок (т. е. exec 2>&1 >log), то весь вывод всё равно останется направленным на терминал, поскольку он будет направлен туда же, куда и поток вывода, а тот, в свою очередь по умолчанию направлен в терминал.
Ради интереса можно попробовать выполнить то же самое, когда поток стандартного вывода не подключён к терминалу. Мы не сможем этого сделать, если поток вывода уже подключён к другому файлу или перенаправлен в конвейер, поскольку таким образом мы нарушим существовавшее перенаправление.
Возьмём, например, команду:
bash test.sh | grep good
То, что нам нужно в конечном итоге, это эквивалент следующей команды:
bash test.sh | tee log | grep good
Вероятно, вашей первой мыслью будет вызвать exec как-то так:
exec | tee log & # Работать не будет
пытаясь таким образом при помощи exec перенаправить вывод в tee, запущенной в фоновом режиме. Но это не будет работать (хотя bash ничего и не скажет). Такая конструкция всего лишь перенаправляет вывод exec в tee, а поскольку exec не выводит ничего, то tee создаст пустой файл и на этом закончит.
Есть ещё одна мысль: запустить tee в фоне, направив в неё ввод из одного файлового дескриптора, а вывод перенапровить в другой. И вы можете это сделать, но проблема в том, что не существует способа создать новый процесс, стандартный ввод которого был бы подключён к каналу. Если бы мы могли это сделать, то получить вывод tee было бы просто, поскольку по умолчанию он направлен туда же, куда и вывод сценария целиком. Таким образом, мы могли бы просто закрыть поток вывода сценария и подключить его к нашему каналу, если бы была возможность этот канал создать.
Получается, мы в тупике? Нет. Решение описано в последних двух предложениях предыдущего абзаца. Нам нужно лишь создать канал, верно? Отлично, давайте использовать именованные каналы.
#!/bin/bash
echo hello
if test -t 1; then
# Stdout is a terminal.
exec >log
else
# Stdout is not a terminal.
npipe=/tmp/$$.tmp
trap "rm -f $npipe" EXIT
mknod $npipe p
tee <$npipe log &
exec 1>&-
exec 1>$npipe
fi
echo goodbye
Здесь, если поток стандартного вывода не подключён к терминалу, мы создаём именованный канал (канал, который располагается в виде файла в файловой системе) при помощи mknod, и при помощи trap удаляем его после завершения работы сценария. Затем мы запускаем tee, связывая его поток ввода с созданным каналом и указываем выполнять запись в файл log. Помните, что tee кроме записи в файл всего полученного из потока ввода, также выводит всё в поток стандартного вывода. Также, вспомните о том, что поток вывода tee направлен туда же, куда и весь вывод сценария, вызывающего tee. Таким образом, весь вывод tee будет попадать туда, куда в данный момент направлен поток вывода нашего сценария, то есть, в перенаправленный пользователем поток вывода или канал конвейера, заданные в момент вызова сценария из командной строки. Так что теперь мы получили стандартный вывод tee там, где нам это нужно: в перенаправление или канал конвейера, определённый пользователем.
Теперь остаётся передать на вход tee нужные данные. Поскольку tee теперь считывает данные из именованного канала, всё что нам необходимо, это перенаправить стандартный вывод в именованный канал. Мы закрываем текущий поток вывода (exec 1>&- ) и открываем его в именованный канал (exec 1>$npipe). Обратите внимание, что закрытие текущего потока вывода ничего не нарушает, поскольку tee также осуществляет вывод в перенаправленный пользователем поток или канал.
Теперь, когда вы выполните команду и направите её вывод в канал к grep, вы получите и стандартный вывод и запись в лог-файле.
Подобных приёмов масса, откройте man-страницу bash!
P.S.
В Bash 4 то же самое можно сделать при помощи конструкции coproc, но об этом в другой раз.
Источник:
http://www.ashep.org/2010/perenapravleniya-v-bash-pri-pomoshhi-exec/
Категория:
Shell
| Добавил:
oleg
(25.09.2010) | Автор:
ashep
Просмотров:
1445
| Рейтинг:
0.0
/
0
|
- Оценить -
Отлично
Хорошо
Неплохо
Плохо
Ужасно
Всего комментариев:
0
Добавлять комментарии могут только зарегистрированные пользователи.
[
Регистрация
|
Вход
]
Форма входа
Друзья сайта
Google+
Copyright MyCorp © 2025