Wie sende ich alle Ausgaben an "Logger" in der POSIX-Shell?

10

Ich möchte Standardausgabe und Standardfehler bei der .xprofileVerwendung separat protokollieren logger. In Bash würde das ungefähr so ​​aussehen:

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

Wie würde ich das auf POSIX- /bin/sh kompatible Weise tun ?

l0b0
quelle

Antworten:

10

Es gibt kein POSIX-Äquivalent. Sie können eine Umleitung nur mit exec, nicht mit einer Gabelung durchführen. Für ein Rohr ist eine Gabel erforderlich, und die Schale wartet darauf, dass das Kind fertig ist.

Eine Lösung besteht darin, Ihren gesamten Code in eine Funktion einzufügen.

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

(Dies protokolliert auch alle Fehler von der stdout-Instanz von logger in die stderr-Instanz. Sie können dies vermeiden, indem Sie mehr Dateideskriptoren mischen.)

Wenn Sie möchten, dass die übergeordnete Shell beendet wird, auch wenn die loggerProzesse noch ausgeführt werden, setzen Sie sie &am Ende der loggerAufrufe.

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

Alternativ können Sie Named Pipes verwenden.

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"
Gilles 'SO - hör auf böse zu sein'
quelle
Dies wird im Syslog als zwei Einträge (out & err) für das gesamte Skript angezeigt. Das Beispiel in der Frage enthält einen (oder zwei) Eintrag pro Befehl.
DarkHeart
@ DarkHeart Ich verstehe deinen Kommentar nicht. Der Code in der Frage und beide von mir vorgeschlagenen Lösungen führen die gleiche Anzahl von Instanzen aus loggerund übergeben jedem die gleiche Eingabe.
Gilles 'SO - hör auf böse zu sein'
Entschuldigung, mein Fehler
DarkHeart
1
Das scheint einfacher zu sein , also gehe ich mit Ihrer Named Pipes-Lösung.
10.
9

POSIX-Befehls- / Prozessersetzung


_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

Sie sollten in der Lage sein, Folgendes zu verwenden:

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

Hier ist eine Version, die den mktempBefehl verwendet:

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

... was fast genauso ist, außer dass es erlaubt mktemp, den Dateinamen für Sie auszuwählen. Dies funktioniert, weil die Prozessersetzung keineswegs magisch ist und der Befehlssubstitution sehr ähnlich ist . Anstatt die Erweiterung durch den Wert des darin ausgeführten Befehls zu ersetzen, wie dies bei der Befehlsersetzung der Fall ist, wird sie durch die Prozessersetzung durch den Namen einer Dateisystemverknüpfung ersetzt, in der sich die Ausgabe befindet.

Während die POSIX-Shell keine direkte Folge davon bietet, ist die Emulation sehr einfach. Alles, was Sie tun müssen, ist, eine Datei zu erstellen, ihren Namen aus einer Befehlsersetzung in den Standard zu drucken und im Hintergrund desselben Ihren Befehl auszuführen, der in diese Datei ausgegeben wird. Jetzt können Sie einfach in den Wert dieser Erweiterung umleiten - genau wie bei der Prozessersetzung. Und so bietet die POSIX-Shell natürlich alle Tools, die Sie benötigen - alles, was Sie benötigen, ist, dass Sie sie auf eine Weise verwenden, die zu Ihnen passt.

Beide oben genannten Versionen stellen sicher, dass die Dateisystemverbindung zu den von ihnen erstellten / verwendeten Pipes zerstört wird, bevor sie jemals verwendet werden. Dies bedeutet, dass nachträglich keine Bereinigung erforderlich ist und, was noch wichtiger ist, ihre Streams nur für die Prozesse verfügbar sind, die sie anfänglich öffnen. Daher können ihre Dateisystem-Links nicht als Mittel zum Aufspüren / Entführen Ihrer Protokollierungsaktivität verwendet werden. Ihre fs-Links im Dateisystem zu belassen, ist eine potenzielle Sicherheitslücke.


Eine andere Möglichkeit besteht darin, es einzuwickeln. Dies kann innerhalb des Skripts erfolgen.

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

Das würde es Ihrem Skript grundsätzlich ermöglichen, sich selbst aufzurufen, wenn dies noch nicht geschehen ist, und Ihnen ein Arbeitsverzeichnis in temporärem Zustand zum Booten zu geben.

mikeserv
quelle
1
Fertig , danke!
10.
@ l0b0 - Ich bin gerade dabei, eine andere Art dieser Art zu aktualisieren ... Es ist allgemeiner gedacht, und ich habe es endlich geschafft, einige Optionen zu verarbeiten, die sich auf E / A-Deskriptoren / -Dateien beziehen.
Mikeserv
@ l0b0 - Wenn Sie mktempund andere nicht standardmäßige Befehle verwenden möchten , kann dies sein : _log()( p=; mkfifo "${p:=$(mktemp -u)}"; printf %s "$p"; exec <&- >&- <>/dev/null >&0; { rm "$p"; logger --priority user."$1" --tag "${0##*/}"; } <"$p" &). Ich habe es in jeder Hinsicht mit einer vollständig portablen Befehlssprache geschrieben - mit Ausnahme Ihrer eigenen. Auch basenameist kein sehr nützliches Dienstprogramm. "${0##*/}"ist sowohl robuster als auch schneller.
Mikeserv
Ich habe es geändert, weil ich es nur brauche, um auf modernen Plattformen zu arbeiten. das Hauptproblem war , dass .xprofile hat mit ausgeführt werden sh.
10.
1
@Wildcard - nur eine Minute und ich werde versuchen, den ersten Teil der Frage zu klären, aber die Antwort auf den zweiten lautet ja: Der zshRandfall ist ein w / Multios aktiviert. Wie für den ersten Teil : { this; can\'t; happen; before; } < this. Hilft das?
Mikeserv