Як відправити весь вихід на `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 ?

Відповіді:


10

Не існує еквівалента POSIX. Ви можете виконувати перенаправлення лише за execдопомогою вилки. Для труби потрібна вилка, і оболонка чекає, коли дитина закінчить.

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

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

(Це також реєструє будь-яку помилку від екземпляра реєстратора stdout до екземпляра 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"

Це з'явиться у syslog у вигляді двох записів (із & помилкою) для всього сценарію. Приклад у запитанні матиме один (або два) записи на команду.
DarkHeart

@DarkHeart Я не розумію ваш коментар. Код у питанні та обидва рішення я пропоную запустити однакову кількість екземплярів loggerі передавати однаковий вхід до кожного з них.
Жил 'ТАК - перестань бути злим'

вибачте, моя помилка
DarkHeart

1
Це здається простішим , тому я збираюся з вашим названим рішенням труб.
l0b0

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

Це в основному дозволить вашому сценарію зателефонувати, якщо він ще не працює, і отримати робочу директорію в темпе для завантаження.


1
Готово , дякую!
l0b0

@ 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.