Я хочу иметь возможность передавать данные между процессами в существующем конвейере bash. Чтобы лучше проиллюстрировать, что я имею в виду, возьмем этот тривиальный пример, где я предполагаю, что файловый дескриптор 3 действует как fifo.
seq 5 | tee >(while read num; do echo $(($num * $num)); done >&3) | paste - <(<&3 cat)
Я хочу, чтобы это вывело
1 1
2 4
3 9
4 16
5 25
, а затем завершить. В идеале решение должно выглядеть примерно так: exec 3<>-
, где bash откроет дескриптор файла три в качестве буфера чтения-записи. Насколько я могу судить, в bash это не поддерживается, поэтому я обратился к этому вопросу для создания анонимных каналов. Основываясь на этом обсуждении, я придумал эту функцию:
function temp_pipe() {
[[ "$1" =~ [3-9] ]] || return 1
PIPE_DIR="$(mktemp -d)"
PIPE="$PIPE_DIR/pipe"
mkfifo "$PIPE"
eval "exec $1<>'$PIPE'"
rm -f "$PIPE"
rmdir "$PIPE_DIR"
}
, который принимает дескриптор файла в качестве аргумента и присоединяет его к анонимному fifo. Это в принципе работает, но я не могу понять, как закрыть fifo, когда я закончу записывать вывод в него. Следующее работает почти так, как задумано:
temp_pipe 3 && seq 5 | tee >(while read num; do echo $(($num * $num)); done >&3) | paste - <(<&3 cat)
в том смысле, что он выводит правильный результат, но вставка остается открытой, поскольку дескриптор файла 3 никогда не закрывается. Я наивно думал, что мог бы добавить exec 3>&-
в конец подпроцесса, чтобы закрыть дескриптор файла и заставить все работать так, как ожидалось, но это не работает. Дескриптор родительского файла не закрывается, потому что операция закрытия выполняется в дочернем процессе, и поэтому закрывает унаследованную ссылку на тот же дескриптор вместо закрытия дескриптора в родительском процессе.
В идеале я ищу решение, в котором будет относительно легко создавать сложные конвейеры без большого количества ручного управления временными fifos или файловыми дескрипторами.