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; }
Что касается решения проблемы, стоящей за вопросом: да, перенаправление и замена процесса - лучший выбор для данного конкретного примера.