Используешь ли пайпы в Unix и для чего
Что проверяют
Насколько уверенно вы живёте в терминале и понимаете ли, что стоит за символом |: два процесса, буфер в ядре и блокировки. Для Go-разработчика следом часто идёт вопрос, как сделать то же самое в коде.
Как отвечать
Начните с практики, пара живых примеров убедительнее определения: kubectl logs deploy/api | grep -i error | tail -n 50, curl -s $URL | jq '.items[].id', частотный разбор sort | uniq -c | sort -rn | head.
Механика: оболочка создаёт анонимный пайп вызовом pipe(), stdout левого процесса и stdin правого смотрят на его концы, процессы работают параллельно. Буфер ограничен (в Linux по умолчанию 64 КиБ): быстрый писатель блокируется, пока читатель не освободит место, так пайп сам создаёт обратное давление. Если читатель закрылся, писатель получает SIGPIPE или ошибку EPIPE. Код возврата конвейера берётся от последней команды, поэтому в скриптах ставят set -o pipefail. Для несвязанных процессов есть именованные пайпы, mkfifo.
В Go три разных инструмента:
exec.Cmd:cmd.StdoutPipe()для потокового чтения вывода внешней команды илиcmd2.Stdin, _ = cmd1.StdoutPipe()для конвейера из двух процессов;os.Pipe(): настоящий пайп ОС с двумя файловыми дескрипторами, нужен при работе с процессами;io.Pipe(): синхронный пайп в памяти между горутинами, без буфера. Удобен, чтобы сжимать или кодировать данные на лету и сразу отдавать в HTTP-запрос, не собирая всё в память.
Где спотыкаются
Берут os.Pipe() для связи горутин. Работает, но это лишние системные вызовы, для такой задачи есть io.Pipe() или канал.
Вызывают cmd.Wait(), не дочитав StdoutPipe. Документация прямо это запрещает: Wait закрывает пайп и часть вывода теряется, а если вывод больше буфера, команда и программа ждут друг друга вечно.
Забывают, что Go-программа, пишущая в закрытый stdout или stderr, по умолчанию завершается по SIGPIPE. На остальных дескрипторах приходит ошибка EPIPE, и её надо обрабатывать, а не игнорировать.