Как я могу получить вывод из одного именованного канала обратно в другой именованный канал? - PullRequest
3 голосов
/ 19 февраля 2012

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

Здесьэто базовая версия скрипта (http://pastebin.com/RMt1FYPc):

#!/bin/bash

PROGNAME=$(basename $(readlink -f $0))
LOG="$PROGNAME.log"
PIPE_LOG="$PROGNAME-$$-log"
PIPE_ECHO="$PROGNAME-$$-echo"

# program output to log file and optionally echo to screen (if $1 is "-e")
log () {
  if [ "$1" = '-e' ]; then 
    shift
    $@ > $PIPE_ECHO 2>&1 
  else 
    $@ > $PIPE_LOG 2>&1 
  fi
}

# create named pipes if not exist
if [[ ! -p $PIPE_LOG ]]; then 
  mkfifo -m 600 $PIPE_LOG
fi
if [[ ! -p $PIPE_ECHO ]]; then 
  mkfifo -m 600 $PIPE_ECHO
fi

# cat pipe data to log file
while read data; do
  echo -e "$PROGNAME: $data" >> $LOG 
done < $PIPE_LOG &

# cat pipe data to log file & echo output to screen
while read data; do
  echo -e "$PROGNAME: $data"
  log echo $data   # this doesn't work
  echo -e $data > $PIPE_LOG 2>&1   # and neither does this
  echo -e "$PROGNAME: $data" >> $LOG   # so I have to do this
done < $PIPE_ECHO &

# clean up temp files & pipes
clean_up () {
  # remove named pipes
  rm -f $PIPE_LOG
  rm -f $PIPE_ECHO
}
#execute "clean_up" on exit
trap "clean_up" EXIT 

log echo "Log File Only"
log -e echo "Echo & Log File"

Я думал, что команды в строках 34 и 35 возьмут $data из $PIPE_ECHO и выведут его в $PIPE_LOG. Но,это не работает. Вместо этого я должен отправить этот вывод непосредственно в файл журнала, не проходя через $PIPE_LOG.

Почему это не работает, как я ожидаю?

РЕДАКТИРОВАТЬ:Я изменил shebang на «bash». Однако проблема та же.

РЕШЕНИЕ: Ответ AH помог мне понять, что я не правильно использовал именованные каналы. С тех пор я решилмоя проблема, даже не используя именованные каналы. Это решение здесь: http://pastebin.com/VFLjZpC3

Ответы [ 2 ]

9 голосов
/ 20 февраля 2012

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

может быть в следующем: потребитель будет читать данные до тех пор, пока их больше не будет. Отсутствие данных означает, что во время вызова read ни один производитель не имеет открытого именованного канала. Это означает, что несколько производителей могут кормить одного потребителя только тогда, когда нет времени без хотя бы одного производителя. Подумайте об этой двери, которая автоматически закрывается: если есть постоянный поток людей, которые всегда держат дверь открытой, передавая ручку двери следующей или проталкивая через нее нескольких человек, дверь открыта. Но как только дверь закрыта, она остается закрытой.

Небольшая демонстрация должна прояснить разницу:

Откройте три снаряда. Первая оболочка:

1> mkfifo xxx
1> cat xxx

вывод не отображается, поскольку cat открыл именованный канал и ожидает данные.

Второй Оболочка:

2> cat > xxx 

нет вывода, потому что этот cat является производителем, который сохраняет именованный канал открытым, пока мы не скажем ему закрыть его явно.

Третий Оболочка:

3> echo Hello > xxx
3>

Этот производитель немедленно возвращается.

Первая оболочка :

Hello

Потребитель получил данные, написал их и - поскольку еще один потребитель держит дверь открытой, продолжает ждать.

Третья оболочка

3> echo World > xxx
3> 

Первая оболочка :

World

Потребитель получил данные, написал их и - поскольку еще один потребитель держит дверь открытой, продолжает ждать.

Вторая оболочка : введите в окно cat > xxx:

And good bye!
(control-d key)
2>

Первая оболочка

And good bye!
1>

Клавиша ^ D закрыла последнего производителя, cat > xxx, и, следовательно, потребитель также вышел.


В вашем случае это означает:

  • Ваша функция log будет пытаться открывать и закрывать трубы несколько раз. Не очень хорошая идея.
  • Обе ваши петли while выходят раньше, чем вы думаете. (проверьте это с помощью (while ... done < $PIPE_X; echo FINISHED; ) &
  • В зависимости от расписания ваших различных производителей и потребителей дверь может иногда захлопываться, а иногда нет - у вас есть встроенное условие гонки. (Для тестирования вы можете добавить sleep 1 в конце log функция.)
  • Вы "testcases" пробуете каждую возможность только один раз - попробуйте использовать их несколько раз (вы будете блокировать, особенно с sleep s), потому что ваш производитель может не найти ни одного потребителя.

Так что я могу объяснить проблемы в вашем коде, но не могу сказать вам решение, потому что неясно, каковы границы ваших требований.

0 голосов
/ 20 февраля 2012

Кажется, проблема в части "данные канала в файл журнала".

Давайте посмотрим: вы используете "&", чтобы поместить цикл в фоновом режиме, я думаю, вы имеете в виду, что он должен работатьпараллельно со вторым циклом.

Но проблема в том, что вам даже не нужен знак «&», потому что, как только больше нет данных в fifo, while..read останавливается.(Тем не менее, вы должны иметь некоторые сначала для первого чтения, чтобы работать).Следующее чтение не зависает, если больше нет данных (что может создать другую проблему: как ваша программа останавливается?).

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

С этим примером можно проверить:

mkfifo foo
while read data; do echo $data; done < foo

Этот скрипт будет зависать, пока вы не напишите что-нибудь из другой оболочки (или bg первой).Но это заканчивается, как только чтение начинает работать.

Редактировать: я тестировал на RHEL 6.2 и работает, как вы говорите (например, плохо!).

Проблема в том, что послезапустив скрипт (скажем, скрипт «а»), у вас остался процесс «а».Так что да, сценарий каким-то образом зависает, как я писал ранее (не тот глупый ответ, как я думал тогда :)).За исключением случаев, когда вы пишете только один журнал (будь то только файл журнала или эхо, в этом случае это работает).

(Это цикл чтения из PIPE_ECHO, который зависает при записи в PIPE_LOG и оставляет процесс, выполняющийся каждый раз).

Я добавил несколько сообщений отладки, и вот что я вижу:

  • только одна строка считывается из PIPE_LOG и после этого цикл заканчивается
  • затем второе сообщение отправляется в PIPE_LOG (после того, как было получено из PIPE_ECHO), но процесс больше не читает из PIPE_LOG => запись зависает.

Когда вы ls -l / proc/ [pid] / fd, вы можете видеть, что fifo все еще открыт (но удален).На самом деле, скрипт завершает работу и удаляет fifo, но есть еще один процесс, использующий его.Если вы не удалите log fifo при очистке и не отследите его, это освободит процесс зависания.

Надеюсь, это поможет ...

...