Как отправить весь вывод `logger` в оболочке POSIX?

10

Я хотел бы регистрировать стандартный вывод и стандартную ошибку отдельно при .xprofileиспользовании logger. В Bash я думаю, что это будет выглядеть примерно так:

exec 1> >(logger --priority user.notice --tag $(basename $0)) \
     2> >(logger --priority user.error --tag $(basename $0))

Как бы я сделал это в POSIX- /bin/sh совместимой манере?

l0b0
источник

Ответы:

10

Там нет эквивалента POSIX. Вы можете выполнять перенаправление только с execпомощью команды fork, а не с помощью fork. Труба требует вилки, а оболочка ждет, пока ребенок закончит.

Одно из решений - поместить весь ваш код в функцию.

all_my_code () {
  
}
{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")"; } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")"

(Это также регистрирует любую ошибку от экземпляра stdout logger до экземпляра stderr. Этого можно избежать, добавив больше файловых дескрипторов.)

Если вы хотите, чтобы родительская оболочка завершилась, даже если loggerпроцессы все еще работают, поместите их &в конец loggerвызовов.

{ all_my_code |
  logger --priority user.notice --tag "$(basename "$0")" & } 2>&1 | 
  logger --priority user.error --tag "$(basename "$0")" &

Кроме того, вы можете использовать именованные каналы.

pipe_dir=$(mktemp -d)
mkfifo "$pipe_dir/out" "$pipe_dir/err"
<"$pipe_dir/out" logger --priority user.notice --tag "$(basename "$0")" &
<"$pipe_dir/err" logger --priority user.error --tag "$(basename "$0")" &
exec >"$pipe_dir/out" 2>"$pipe_dir/err" 

rm -r "$pipe_dir"
Жиль "ТАК - перестань быть злым"
источник
Это будет отображаться в системном журнале как две записи (out & err) для всего сценария. Пример в вопросе будет иметь одну (или две) запись на команду.
DarkHeart
@ DarkHeart Я не понимаю ваш комментарий. Код в вопросе и оба решения, которые я предлагаю, запускают одинаковое количество экземпляров loggerи передают одинаковые входные данные каждому из них.
Жиль "ТАК - перестань быть злым"
извините, моя ошибка
DarkHeart
1
Это кажется проще , поэтому я собираюсь использовать ваше решение для именованных каналов.
10
9

Подстановка команды / процесса POSIX


_log()( x=0
    while  [ -e "${TMPDIR:=/tmp}/$$.$((x+=1))" ]
    do     continue; done        &&
    mkfifo -- "$TMPDIR/$$.$x"    &&
    printf %s\\n "$TMPDIR/$$.$x" || exit
    exec >&- >/dev/null
    {  rm -- "$TMPDIR/$$.$x"
       logger --priority user."$1" --tag "${0##*/}"
    }  <"$TMPDIR/$$.$x" &
)   <&- </dev/null

Вы должны быть в состоянии использовать это как:

exec >"$(_log notice)" 2>"$(_log error)"

Вот версия, которая использует mktempкоманду:

_log()( p=
    mkfifo "${p:=$(mktemp -u)}"    &&
    printf %s "$p"                 &&
    exec  <&- >&- <>/dev/null >&0  &&
    {   rm "$p"
        logger --priority user."$1" --tag "${0##*/}"
    }   <"$p" &
)

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

В то время как оболочка POSIX не обеспечивает прямого следствия для такой вещи, эмулировать это очень просто. Все, что вам нужно сделать, это создать файл, вывести его имя из стандарта из подстановки команд и в фоновом режиме запустить вашу команду, которая будет выводить в этот файл. Теперь вы можете просто перенаправить на значение этого расширения - точно так же, как вы делаете с подстановкой процесса. И поэтому оболочка POSIX предоставляет все инструменты, которые вам, конечно, нужны - все, что вам нужно, - это использовать их так, как вам удобно.

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


Другой способ - обернуть его. Это можно сделать из скрипта.

x=${x##*[!0-9]*}
_log(){ 
    logger --priority user."$1" --tag "${0##*/}"
}   2>/dev/null >&2

cd ../"$PPID.$x" 2>/dev/null &&
trap 'rm -rf -- "${TMPDIR:-/tmp}/$PPID.$x"' 0 || 
{   until cd -- "${TMPDIR:=/tmp}/$$.$x"
    do    mkdir -- "$TMPDIR/$$.$((x+=1))"
    done  && 
    x=$x "$0" "$@" | _log notice
    exit
}   2>&1 | _log error

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

mikeserv
источник
1
Готово , спасибо!
10
@ l0b0 - Я собираюсь обновить еще один вид, подобный этому ... Он более универсален, и я наконец-то нашел способ дать ему возможность обрабатывать некоторые параметры, связанные с дескрипторами / файлами ввода / вывода.
mikeserv
@ l0b0 - если вы хотите использовать mktempи другие нестандартные команды, это может быть: _log()( p=; mkfifo "${p:=$(mktemp -u)}"; printf %s "$p"; exec <&- >&- <>/dev/null >&0; { rm "$p"; logger --priority user."$1" --tag "${0##*/}"; } <"$p" &). Я написал это, используя полностью переносимый командный язык во всех отношениях - за исключением вашего собственного. Также basenameне очень полезная утилита. "${0##*/}"и надежнее, и быстрее.
mikeserv
Я изменил его, потому что он мне нужен только для работы на современных платформах; основной вопрос заключается в том , что .xprofile должно быть выполнено с sh.
l0b0
1
@Wildcard - минутку, и я постараюсь разобраться в первой части вопроса, но ответ на второй - да: в zshкрайнем случае включен w / multios. Что касается первой части: { this; can\'t; happen; before; } < this. Это помогает?
mikeserv