Bash Coproc и оставшийся вывод Coproc

Мне нужно прочитать некоторые данные конфигурации в переменные среды в скрипте bash.

"Очевидный" (но неверный) шаблон:

egrep "pattern" config-file.cfg | read VAR1 VAR2 VAR3 etc...

Это не удается, потому что read запускается в подоболочке и поэтому не может устанавливать переменные в вызывающей оболочке. Так что я придумал это как альтернативу

coproc egrep "pattern" config-file.cfg
read -u ${COPROC[0]} VAR1 VAR2 VAR3 etc...

который отлично работает

Чтобы проверить, что происходит, если сопроцесс возвращает более одной строки, я попробовал это:

coproc cat config-file.cfg
read -u ${COPROC[0]} VAR1 VAR2 VAR3 etc...

где config-file.cfg содержит три строки.

$ cat config-file.cfg
LINE1 A1 B1 C1
LINE2 A2 B2 C2
LINE3 A3 B3 C3

Я ожидал, что это обработает первую строку в файле, после чего появится какое-то сообщение об ошибке "сломанная труба". Несмотря на то, что он обработал первую строку, сообщения об ошибке не было и сопроцессор не был запущен.

Тогда я попробовал следующее в скрипте:

$ cat test.sh
coproc cat config-file.cfg
read -u ${COPROC[0]} VAR1 VAR2 VAR3 VAR4
echo $VAR1 $VAR2 $VAR3 $VAR4
wait
echo $?

Запуск это:

$ bash -x test.sh
+ read -u 63 VAR1 VAR2 VAR3 VAR4
+ cat config-file.cfg
LINE1 A1 B1 C1
+ wait
+ echo 0
0

Куда делись оставшиеся две строки? Я бы ожидал либо "сломанную трубу", либо wait повесить, так как не было ничего, чтобы прочитать оставшиеся строки, но, как вы можете видеть, код возврата был нулевым.

2 ответа

Решение

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

read VAR1 VAR2 VAR3 < <(egrep "pattern" config-file.cfg)

"Если используется форма <(list), файл, переданный в качестве аргумента, должен быть прочитан для получения вывода списка" - о каком "файле, переданном в качестве набора", они говорят?

Это довольно загадочно для меня тоже. Глава о замене процессов в Advanced Bash-scripting Guide имеет более полное объяснение.

То, как я это вижу, когда <(cmd) синтаксис используется, выход cmd становится доступным через именованный канал (или временный файл), а синтаксис заменяется именем файла канала / файла. Таким образом, для приведенного выше примера это будет эквивалентно:

read VAR1 VAR2 VAR3 < /dev/fd/63

где /dev/fd/63 это именованная труба, подключенная к стандартному выводу cmd,

Если я правильно понимаю ваш вопрос (и я надеюсь, что я не утверждаю очевидное),прочитайте чтение читает по одной строке за раз, как в:

$ read a b c < config-file.cfg && echo $?
0

или же:

$ printf '%s\n%s\n' one two | { read; echo "$REPLY";}
one

$ echo ${PIPESTATUS[@]}
0 0

Чтобы прочитать все входные данные вам понадобится цикл:

$ coproc cat config-file.cfg
[1] 3460

$ while read -u ${COPROC[0]} VAR1 VAR2 VAR3; do echo $VAR1 $VAR2 $VAR3; done
LINE1 A1 B1 C1
LINE2 A2 B2 C2
LINE3 A3 B3 C3
[1]+  Done                    coproc COPROC cat config-file.cfg

Просто чтобы добавить, что это объясняется в FAQ.

Что происходит, как только завершается подоболочка, родительская оболочка очищает и закрывает FD. Тебе повезло, что ты даже прочитал первую строчку!

Попробуйте это в интерактивной оболочке:

$ coproc ECHO { echo foo; echo bar; }
[2] 16472
[2]+  Done                    coproc ECHO { echo foo; echo bar; }
$ read -u ${ECHO[0]}; echo $REPLY
bash: read: -u: option requires an argument
read: usage: read [-ers] [-a array] [-d delim] [-i text] [-n nchars] [-N nchars] [-p prompt] [-t timeout] [-u fd] [name ...]

Это даже уничтожает переменную окружения.

Теперь попробуйте это:

$ coproc ECHO { echo foo; echo bar; sleep 30; }
[2] 16485
$ read -u ${ECHO[0]}; echo $REPLY
foo
$ read -u ${ECHO[0]}; echo $REPLY
bar
$ read -u ${ECHO[0]}; echo $REPLY # blocks until the 30 seconds are up

[2]+  Done                    coproc ECHO { echo foo; echo bar; sleep 30; }

Что касается решения проблемы, стоящей за вопросом: да, перенаправление и замена процесса - лучший выбор для данного конкретного примера.

Другие вопросы по тегам