Bash перенаправить весь вывод на именованные каналы - PullRequest
0 голосов
/ 05 сентября 2018

Я искал способ использовать перенаправление Bash для перенаправления всех выходов (1 (STDOUT), 2 (STDERR), 3 и т. Д.) На именованные каналы. Вот скрипт, который я написал для проверки этой теории:

#!/bin/bash

pipe1="/tmp/pipe1"
pipe2="/tmp/pipe2"
pipe3="/tmp/pipe3"

mkfifo "${pipe1}"
mkfifo "${pipe2}"
mkfifo "${pipe3}"

trap "rm -rf ${pipe1} ${pipe2} ${pipe3}" EXIT

printer() {
  echo "OUT" >&1
  echo "ERR" >&2
  echo "WRN" >&3
}

# Usage: mux
mux() {
  cat "${pipe1}"
  cat "${pipe2}"
  cat "${pipe3}"
}

printer 1>"${pipe1}" 2>"${pipe2}" 3>"${pipe3}"
mux

Этот код вроде бы в порядке, но терминал зависает до тех пор, пока не завершится. Насколько я понимаю, каналы похожи на файлы в том смысле, что они имеют индекс, но вместо записи на диск они просто записывают в память.

При этом, должен быть доступным, как и любой другой файл. Я знаю, что скрипт зависает на линии, вызывающей функцию принтера. Я также протестировал несколько комбинаций подоболочек и более продвинутых перенаправлений (а именно, перенаправление на STDOUT для обработки каждого из других каналов). Возможно, мне не хватает терминатора в именованном канале (из-за чего он заблокирован и не может быть доступен функцией mux). Если это так, как это достигается?

EDIT После дополнительного тестирования выясняется, что проблема возникает только при попытке перенаправления с нескольких каналов. Например:

#!/bin/bash

pipe1="/tmp/pipe1"
mkfifo "${pipe1}"
trap "rm -rf ${pipe1}" EXIT

(exec >"${pipe1}"; echo "Test") &
cat < "${pipe1}"

будет работать как положено. Однако, добавив STDOUT (например), это сломается, заставив его зависнуть:

#!/bin/bash

pipe1="/tmp/pipe1"
mkfifo "${pipe1}"
trap "rm -rf ${pipe1}" EXIT

(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}"
cat < "${pipe2}"

Точнее говоря, код зависает после выполнения оператора exec >"${pipe1}" 2>"${pipe2}. Я предполагаю, что добавление большего количества подоболочек в определенных местах поможет, но это может стать грязным / громоздким. Тем не менее я узнал, что именованные каналы предназначены для соединения данных между оболочками (отсюда добавляются вспомогательные оболочки и оператор фона &).

1 Ответ

0 голосов
/ 06 сентября 2018

Если вы хотите иметь возможность читать содержимое после закрытия дескриптора файла, вам нужно просто использовать файлы. Мысль о каналах заключается в том, что команда чтения должна выполняться в первую очередь перед командой, которая пишет.

При такой настройке:

cmd1 | cmd2 | cmd3
Сначала запускается

cmd3, затем cmd2, затем cmd1. Поэтому, если вы хотите настроить его с помощью каналов, вам нужно будет открыть каждый fifo для параллельного чтения, а затем вызвать printer:

printer() {
  echo "OUT" >&1
  echo "ERR" >&2
  echo "WRN" >&3
}

# Usage: mux
mux() {
  cat "${pipe1}" &
  cat "${pipe2}" &
  cat "${pipe3}"
}

mux &
printer 1>${pipe1} 2>"${pipe2}" 3>"${pipe3}"

Оболочка будет блокироваться по этому фрагменту:

(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}"
cat < "${pipe2}"

Вкл. cat < "$pipe1" Причина, по которой необходимо выполнить чтение из обоих каналов, чтобы exec продолжил:

(exec >"${pipe1}" 2>"${pipe2}"; echo "Test"; echo "Test2" >&2) &
cat < "${pipe1}" &
cat < "${pipe2}"

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

В качестве обходного пути вы можете использовать внутреннюю буферизацию bash pipe для буферизации ваших сообщений:

printer() {
  echo "OUT" >&3
  echo "ERR" >&4
  echo "WRN" >&5
}

# Usage: mux
mux() {
  timeout 1 cat "${pipe1}"
  timeout 1 cat "${pipe2}"
  timeout 1 cat "${pipe3}"
}

printer 3> >(cat >$pipe1) 4> >(cat >$pipe2) 5> >(cat >$pipe3)
mux

Что здесь происходит, так это то, что каналы всегда открыты для записи, даже после того, как функция принтера существует, и будут оставаться открытыми, пока не будет запущена замена процесса. Вы можете закрыть их вручную, с помощью exec 5>&-, что запишет EOF в канал, позволяя cat $pipe3 вернуться нормально. cat "$pipe1" никогда не завершится, если функция не закрывает файловые дескрипторы, поэтому используются функции тайм-аута, чтобы мы могли сливать каналы, не блокируя их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...