Как получить стандартный вывод функции, которая изменяет текущую среду?

find [...] -exec utilizará la ruta (relativa )a los archivos encontrados. Si desea usar -execcon la suposición de que el archivo encontrado está en el directorio actual, en su lugar desea usar -execdir.

2
16.06.2019, 03:21
3 ответа

И Bash, и zsh имеют сопроцессы (, к сожалению, немного разные ), которые, по сути, оборачивают вызов pipeи порождают подпроцесс с доступными как стандартным вводом, так и стандартным выводом. к вызывающей оболочке. По сути, они позволяют оболочке воспроизводить как x, так и yв x | cmd | y, но при этом все x, yи cmdвыполняются из текущего процесса, а , а не в среда выполнения конвейера.

Это позволит нам запустить module 2>&...для некоторого ..., при этом moduleбудет работать из текущей оболочки. Если мы используем catв качестве нашего сопроцесса (, то есть cmd), он просто повторит все снова, а затем мы сможем снова прочитать вывод в текущую оболочку с помощью y <&...позже.

Другой вариант — перенаправить стандартную ошибку в другой фоновый процесс и waitдля его кода возврата. Я обращусь к обоим ниже.


Я собираюсь использовать эту фальшивую moduleфункцию для тестирования, чтобы я мог включать и выключать ошибки по своему желанию. If изменяет текущую среду оболочки, чтобы мы могли ее видеть, и выводит «err» в stderr; Я буду комментировать эту строку по мере необходимости:

module() {
        sleep 1
        FOO=$(date)
        echo err >&2
} 

Если я запускаю сопроцесс catв Bash, я могу перенаправить modulestderr на него и читать из catstdout, чтобы делать все, что захочу:

coproc cat
module 2>&${COPROC[1]}
exec {COPROC[1]}>&-
if grep -q err <&${COPROC[0]}
then
        echo got an error
else
        echo no error
fi

В zsh должно быть

module 2>&p
exec 4<&p
coproc :
if grep -q err <&4

вместо этого посередине.

В любом случае я могу запустить команду moduleв текущей оболочке и прочитать вывод об ошибке.Функция может возвращаться в пределах ifкак обычно.

Все, кроме cat, выполняется из текущей среды выполнения. :Перенаправления FD не создают независимые среды, как это делают конвейеры. Мы можем echo $FOOв конце проверить это и увидеть, что дата обновилась, потому что moduleработал в текущей среде.


В качестве альтернативы, фоновый процесс может выполнять всю работу. Это работает в Bash:

exec 2> >( if grep -q. ; then exit 7 ; else exit 0 ; fi )
PID=$!
module
exec 2>&-
wait $PID
echo $?

Приведенное выше выведет либо 7, либо 0в зависимости от того, что сказал подпроцесс вверху — вы можете настроить, чтобы делать с кодом возврата все, что вам нравится. В zsh это не так, потому что $!не установлен для замены процессов; это должно быть решаемо, но я перестал пытаться. Здесь также подойдет фиксированный fifo, а не временный файл.

В этом случае вы, вероятно, захотите сохранить и восстановить FD 2 на любой стороне.

4
27.01.2020, 21:49

Я не знаю, как работают «Модули среды», но я предполагаю из вашего описания, что это функции оболочки, которые устанавливают переменные среды, и их вывод stderr должен перехватываться/сопоставляться без запуска их в отдельном обработать.

Ответ, нравится вам это или нет, заключается в том, что единственный надежный и очевидный способ — перенаправить их stderr во временный файл. Использование именованных каналов столь же громоздко (, что вам все равно придется создавать временный файл! ), кроме того, что это намного сложнее. А использование co -процессов обременительно, неудобно и непереносимо .

Вbash(и только в bash)вы можете воспользоваться преимуществами недокументированной функции ($!, устанавливаемой на PID из >(...)подстановки процесса )и выйти сухим из воды. как:

module 2> >(grep error)
wait $! && echo failed

В этом примере предполагается, что moduleсам по себе не порождает дочерних элементов, которые могут запутать $!.

4
27.01.2020, 21:49

В Linux, как с bash, так и с zsh, вы сможете сделать:

my_module() {
  chmod u+w /dev/fd/3 # only needed in bash 5+
  module 2> /dev/fd/3 3>&-
  ! grep -q err /dev/fd/3
} 3<<< ''

3<<< ''— это здесь -строка, первоначально содержащая пустую строку. И zsh, и bashреализуют здесь -строки, а здесь -документы в виде удаленных временных файлов. В Linux (и Cygwin, но, как правило, не в других системах ), открытие /dev/fd/3открывает файл, на который указывает fd 3, даже если он уже удален (в других системах, он дублирует fd 3 )так что это очень чистый способ работы с временными файлами. Файл уже удален, вам не нужно беспокоиться о его очистке, и он виден на ФС только очень короткое время (начиная с версии 5, однако bashудаляет права на запись в него, которые нам нужны для работы. вокруг сchmod).

Здесь вам нужен временный файл, если вы собираетесь запускать moduleи grepпоследовательно (, а не параллельно в отдельных процессах ). Выполнение этого с каналами (, как в подходах Майкла, но в отличие от @mosvy ), приведет к мертвым -блокировкам, если будет достаточно выходных данных, чтобы заполнить каналы.

4
27.01.2020, 21:49

Теги

Похожие вопросы