Тройник с недоразумением подстановки процесса

Я пытаюсь написать красивый принтер для записей LDAP, который только один раз извлекает корневую запись LDAP, а затем направляет вывод в tee, который вызывает красивый принтер для каждого раздела.

Для иллюстрации скажем, что моя функция group_entry возвращает LDIF определенного DN LDAP. Детали которых не важны, поэтому допустим, что он всегда возвращается:

dn: cn=foo,dc=example,dc=com
cn: foo
owner: uid=foo,dc=example,dc=com
owner: uid=bar,dc=example,dc=com
member: uid=foo,dc=example,dc=com
member: uid=baz,dc=example,dc=com
member: uid=quux,dc=example,dc=com
custom: abc123

Я могу легко выделить владельцев и участников по отдельности с помощью grep и cuting. Затем я могу передать эти вторичные DN в другой поисковый запрос LDAP, чтобы получить их настоящие имена. В качестве примера предположим, что у меня есть функция pretty_print, которая параметризуется по имени атрибута LDAP, которая выполняет все, что я только что упомянул, а затем красиво форматирует все с помощью AWK:

$ group_entry | pretty_print owner
Owners:
foo    Mr Foo
bar    Dr Bar

$ group_entry | pretty_print member
Members:
foo    Mr Foo
baz    Bazzy McBazFace
quux   The Artist Formerly Known as Quux

Они отлично работают по отдельности, но когда я пытаюсь tee их вместе, ничего не происходит:

$ group_entry | tee >(pretty_print owner) | pretty_print member
Members:
[Sits there waiting for Ctrl+C]

Очевидно, я неправильно понимаю, как это должно работать, но это ускользает от меня. Что я делаю неправильно?


ИЗМЕНИТЬ. Вот мой полный сценарий:

#!/usr/bin/env bash

set -eu -o pipefail

LDAPSEARCH="ldapsearch -xLLL"

group_entry() {
  local group="$1"
  ${LDAPSEARCH} "(&(objectClass=posixGroup)(cn=${group}))"
}

get_attribute() {
  local attr="$1"
  grep "${attr}:" | cut -d" " -f2
}

get_names() {
  # We strip blank lines out of the LDIF entry, then we always have "dn"
  # followed by "cn" records; we strip off the attribute name and
  # concatenate those lines, then sort. So we get a sorted list of:
  # {{distinguished_name}} {{real_name}}
  xargs -n1 -J% ${LDAPSEARCH} -s base -b % cn \
  | grep -v "^$" \
  | cut -d" " -f2- \
  | paste - - \
  | sort
}

pretty_print() {
  local attr="$1"
  local -A pretty=([member]="Members" [owner]="Owners")

  get_attribute "${attr}" \
  | get_names \
  | gawk -F'\t' -v title="${pretty[${attr}]}:" '
    BEGIN { print title }
    { print "-", gensub(/^uid=([^,]+),.*$/, "\\1", "g", $1), "\t", $2 }
  '
}

# FIXME I don't know why tee with process substitution doesn't work here
group_entry "$1" | pretty_print owner
group_entry "$1" | pretty_print member

person Xophmeister    schedule 13.03.2017    source источник
comment
Если вы можете поделиться реальным кодом, который вы пытаетесь + ваш неправильный результат + ожидаемый результат, нам будет легко помочь вам лучше.   -  person Inian    schedule 13.03.2017
comment
Еще одна проблема заключается в том, что вызов pretty_print owner наследует стандартный вывод из того же места, что и tee, что означает, что pretty_print member получит дополнительный неожиданный ввод.   -  person chepner    schedule 13.03.2017
comment
Также существует проблема с двумя вызовами pretty_print, выполняющимися асинхронно, поэтому их вывод, вероятно, будет чередоваться, если они записывают в один и тот же файл.   -  person chepner    schedule 13.03.2017
comment
(Я добавил свой скрипт сейчас)   -  person Xophmeister    schedule 13.03.2017


Ответы (1)


Описанное вами поведение очень похоже на ситуацию, которая может возникнуть в программе на C, которая разветвляет и выполняет другую программу (как оболочка и xargs, безусловно, делают) без должной обработки всех дескрипторов открытых файлов. Вы можете остаться в ситуации, когда процесс p1 не завершается, потому что он ожидает наблюдения EOF на своем стандартном вводе, но этого никогда не произойдет, потому что другой процесс p2 содержит дескриптор открытого файла для конца записи канала, который обеспечивает стандартный ввод p1, а p2 сам ожидает завершения или выполнения p1 какое-то другое действие.

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

echo "foo" | tee >(cat) | cat

... в версии 4.2.46 из bash. Может случиться так, что в вашей версии bash (даже если она такая же) или в xargs есть связанная ошибка, но это предположение. Я не думаю, что ваш конвейер должен зависать, как вы говорите, но я не готов начать указывать пальцем.

В любом случае, даже если ваш конвейер не завис, у него нет нужной семантики, как отметил @chepner в комментариях. pretty_print member получит вывод tee на свой стандартный ввод, и он будет включать оба вывода group_entry и вывода pretty_print owner. Вы можете подумать о реализации этого по-другому: поскольку тройник может мультиплексировать ввод более чем двумя способами, вы можете убить двух зайцев одним выстрелом:

group_entry "$1" | tee >(pretty_print owner) >(pretty_print member)

Но это оставляет открытой возможность того, что результат двух pretty_print выполнений будет смешан, а также будет повторять вывод group_entry. Вы могли бы отфильтровать вывод group_entry, но чтобы избежать смешения, вам нужно убедиться, что две pretty_print команды выполняются последовательно. Это представляет проблему для подхода, основанного на tee, потому что, если какой-либо из выходов tee блокируется, весь конвейер может остановиться.

Одно из решений - перенаправить вывод одной или обеих pretty_print команд в файл. В качестве альтернативы, если важно, чтобы оба вывода направлялись в стандартный вывод, я не вижу хорошей альтернативы, кроме как захватить вывод group_entry и передать его отдельно каждому pretty_print заданию. Вы можете записать его в файл, но это ненужно и немного беспорядочно. Вместо этого подумайте об этом:

entry_lines=$(group_entry "$1")
pretty_print owner  <<<"$entry_lines"
pretty_print member <<<"$entry_lines"

Он использует подстановку команд для захвата вывода group_entry в переменной оболочки (включая символы новой строки) и использует строку здесь для воспроизведения ее в каждом pretty_print процессе.

person John Bollinger    schedule 13.03.2017