У меня есть определенный bash-скрипт, который хочет сохранить исходное /dev/stdout
местоположение перед заменой первого файлового дескриптора другим местоположением.
Поэтому, естественно, я написал что-то вроде
old_stdout=$(readlink -f /dev/stdout)
И это не сработало. Очень быстро я понимаю, в чем проблема:
test@ubuntu:~$ echo $(readlink -f /dev/stdout)
/proc/5175/fd/pipe:[31764]
test@ubuntu:~$ readlink -f /dev/stdout
/dev/pts/18
Obvioulsly, $()
работает в подоболочке, которая передается в родительскую оболочку.
Поэтому возникает вопрос: есть ли надежный (ограниченный переносимость между дистрибутивами Linux) способ сохранить /dev/stdout
местоположение в виде строки в скрипте bash?
bash
io-redirection
io
alexey.e.egorov
источник
источник
/dev/stdout
решит проблему с печатью сообщений в режиме без вывода сообщений. Альтернативой является перенаправление любого другого действия, которое производит выходные данные, и их довольно много. Примерно в 100 раз больше сообщений взаимодействия с пользователем.stderr
. Это, например, почему приглашения собираютсяstderr
по умолчанию.stderr
они также должны быть перенаправлены и сохранены, так как скрипт вызывает несколько внешних программ, и все возможные сообщения об ошибках должны собираться и регистрироваться.Ответы:
Чтобы сохранить дескриптор файла, вы дублируете его на другом диске. Сохранение пути к соответствующему файлу недостаточно, вам нужно сохранить режим открытия, флаги открытия, текущую позицию в файле и так далее. И, конечно же, для анонимных каналов или сокетов это не сработает, поскольку у них нет пути. Вы хотите сохранить описание открытого файла , на которое ссылается fd, а дублирование fd фактически возвращает новый fd к тому же описанию открытого файла .
Чтобы дублировать файловый дескриптор на другой с помощью Bourne-подобной оболочки, синтаксис:
Выше fd 1 дублируется на fd 3.
То, что fd 3 уже было открыто до того, будет закрыто, но учтите, что fds 3–9 (обычно больше, до 99 с
yash
) зарезервированы для этой цели (и не имеют специального значения, противоположного 0, 1 или 2), Оболочка знает, чтобы не использовать их для собственного внутреннего бизнеса. Единственная причина, по которой fd 3 был бы открыт заранее - это то, что вы сделали это в сценарии 1 или он был пропущен вызывающей стороной.Затем вы можете изменить стандартный вывод на другое:
И позже, чтобы восстановить стандартный вывод:
(
3>&-
чтобы закрыть дескриптор файла, который нам больше не нужен).Теперь проблема в том, что кроме ksh, каждая команда, которую вы запускаете после этого
exec 3>&1
, наследует этот fd 3. Это утечка fd. Как правило, не имеет большого значения, но это может вызвать проблемы.ksh
устанавливает флаг close-on-exec для этих fds (для fds более 2), но никакие другие оболочки и другие оболочки не имеют никакого способа установить этот флаг вручную.Обходной путь для другой оболочки - закрыть fd 3 для каждой команды, например:
Громоздкие. Здесь лучшим способом было бы вообще не использовать
exec
, а перенаправлять группы команд:Там это оболочка, которая заботится о сохранении стандартного вывода и его последующем восстановлении (и делает это внутренне, дублируя его на fd (выше 9, выше 99 для
yash
) с установленным флагом close-on-exec ).Примечание 1
Теперь управление этими fds с 3 по 9 может быть громоздким и проблематичным, если вы используете их широко или в функциях, особенно если ваш скрипт использует какой-то сторонний код, который, в свою очередь, может использовать эти fds.
Некоторые оболочки (
zsh
,bash
,ksh93
, все добавили функцию ( предложенный Oliver Киддл изzsh
) примерно в то же время в 2005 году после того, как он был обсужден среди их разработчиков) имеют альтернативный синтаксис , чтобы назначить первый свободный FD выше 10 вместо , который помогает в этом случае:источник
rc.local
службы, например, так что вы действительно должны использовать что-то подобноеexec {FD}>&1
или что- то подобное . Но это поддерживается только в bash 4, что очень печально. Так что это не совсем переносимо.eval "exec $i>&1"
- это то, чего я хотел бы избежать из-за громоздкости. Могу ли я на самом деле полагаться на что FDS выше 9 будет нахаляву?bash
позволит вам выстрелить себе в ногу.Как видите, скрипты bash не похожи на обычный язык программирования, где вы можете назначать файловые дескрипторы.
Самое простое решение - использовать вспомогательную оболочку для запуска того, что вы хотите перенаправить, чтобы обработка могла быть возвращена к верхней оболочке, которая имеет стандартный ввод / вывод без изменений.
Альтернативным решением будет использование
tty
идентификации устройства TTY и управление вводом / выводом в вашем сценарии. Например:и тогда ты сможешь ..
источник
$$
получит PID текущего процесса, в случае интерактивной оболочки или скрипта соответствующий PID оболочки.Таким образом, вы можете использовать:
Пример:
источник
/proc
структуры вызывает проблемы переносимости, как и использование,/dev/stdout
как упомянуто в вопросе./proc
структуру? Это будет работать на любом Linux, который имеетprocfs
..procfs
это почти всегда присутствует, но мы часто видим вопросы переносимости, и хорошая методология разработки включает рассмотрение переносимости на другие системы.bash
может работать на множестве операционных систем.