Я использую (с закрытым исходным кодом) специфичный для поставщика инструмент сборки (Microsemi Designer, инструмент компоновки ПЛИС). Я вызываю его из скрипта оболочки (сильно упрощенно):
... # Setup
/opt/.../designer SCRIPT:my_script.tcl |tee build.log
... # Postprocessing
Проблема в следующем: даже после designer
завершения процесса он tee
продолжает работать, и, таким образом, выполнение скрипта не продолжается.
Насколько я понимаю, это происходит потому, что designer
порождает долгоживущий демон,
windu_scmd50
, и этот демон не закрывает свой стандартный вывод (который он наследует от designer
). Так же как tee
и ожидание EOF на его стандартном вводе (подключенном к каналу), который так и не приходит.
Доказательства того, что это является причиной проблемы:
ps
показывает, что процесс создания тройника все еще выполняется, а процесс проектирования — нет.- Когда я завершаю
windu_scmd50
процесс,tee
он немедленно завершается, а скрипт продолжается (но я не могу сделать это в продакшене). - Если я не передаю вывод в
designer
(tee
или куда-либо еще; то же самое происходит и сcat
in вместоtee
), скрипт не останавливается. - Если я запускаю
designer
из Jenkins (без перенаправления вывода в файл), Jenkins уведомляет меня о том, что «Процесс допустил утечку файловых дескрипторов», ссылаясь на эта страница помощи(судя по всему, Дженкинс конкретно рассматривает эту ситуацию).
Так:Как я могу гарантировать, что tee
процесс завершится в тот момент (или вскоре после этого) designer
?
Вот некоторые подходы, которые я рассмотрел:
- Можно ли перенаправить stdout запущенного процесса?
- Есть ли инструмент, который я могу использовать вместо трубы-оболочки и который определит завершение процесса
designer
?
Некоторые подходы, которые невозможны:
- Я не могу игнорировать вывод процесса
designer
; мне нужно записать его в файл журнала. - Я не могу завершить
windu_scmd50
процесс, так как он может использоваться другими процессами на машине. - Обходные пути, описанные настраница помощи Jenkinsпохоже, не применяются, поскольку они также заглушат вывод из
designer
. - Я не могу изменить установку инструмента, чтобы заменить
windu_scmd50
исполняемый файл оболочкой.
Еще несколько заметок:
- Было бы неплохо регистрировать вывод из
windu_scmd50
процесса, но это не обязательно. Однако мне нужно регистрировать вывод из процесса дизайнера. designer
На практике между и есть еще один фильтр,tee
который добавляет временные метки (designer ... |ts -s |tee build.log
)- Использование
bash
специфических функций допустимо; совместимость сsh
не является обязательным требованием.
Вот минимальный пример, демонстрирующий проблему ( test.sh
):
#!/bin/bash
echo "Start"
sleep 10 & # Background process that does not close stdout
echo "End"
Вызов этого скрипта напрямую ( ./test.sh
) печатает "Start" и "End" и немедленно завершается. Передача вывода другому процессу ( ./test.sh |cat
) печатает "Start" и "End" немедленно, но затем делает паузу в 10 секунд перед завершением.
решение1
Довольно простой обходной путь — ввести сообщение в поток после designer
завершения процесса. cat
Предшествующий фильтр типа - tee
должен выйти, когда встретит сообщение.
Это будет что-то вроде:
{ /opt/.../designer SCRIPT:my_script.tcl; echo message; } \
| sed -n '/^message$/q;p' | tee build.log
Примечания:
Если
designer
(или что-либо, что наследует его stdout) выводит сообщение, тоsed
преждевременно завершит работу. ВыберитеsTr1ng_Unlik3ly t0-Be enCOUnTered_in vvhat designer PRinT5
. Символ ASCII EOT (восьмеричное 004) может быть хорошим выбором:
{ …; printf '\004\n'; } | sed -n "/^$(printf '\004')\$/q;p" | …
Есть пределы тому, что
sed
можно выдержать. Смотритеэтот ответ оgrep
, это похоже наsed
.^message$
соответствует полной строке, содержащейmessage
только. Это предполагает, что вывод (если есть) процессаdesigner
заканчивается символом новой строки (т.е. последняя строка завершена; см.линияпротивнеполная строка); только тогдаmessage
будет в своей собственной строке. Еслиdesigner
генерирует неполную строку наверняка, то вы хотитеsed -n '/message$/{s/message$//;p;q};p'
. Если это может быть любым способом, то вы хотитеsed -n '/^message$/q;/message$/{s/message$//;p;q};p'
. Обратите внимание, что неполная строка изdesigner
станет полной строкой.Немолчаливые потомки
designer
могут вмешиваться:- Они могут вызвать
message
появление средней строки (даже если они пытаются вывести полные строки). Для нашегоsed
это будет как будтоdesigner
сгенерирована неполная строка. - Если выбранное вами сообщение очень длинное, то оно может появиться в конвейере вместе с другими данными (см.этот ответи
PIPE_BUF
здесь). В таком случае мыsed
его вообще не обнаружим.
- Они могут вызвать