Wenn wir die Idee von @Fred ein wenig weiter verbessern, könnten wir auf diese Weise eine kleine Protokollierungsbibliothek erstellen:
declare -A _log_levels=([FATAL]=0 [ERROR]=1 [WARN]=2 [INFO]=3 [DEBUG]=4 [VERBOSE]=5)
declare -i _log_level=3
set_log_level() {
level="${1:-INFO}"
_log_level="${_log_levels[$level]}"
}
log_execute() {
level=${1:-INFO}
if (( $1 >= ${_log_levels[$level]} )); then
"${@:2}" >/dev/null
else
"${@:2}"
fi
}
log_fatal() { (( _log_level >= ${_log_levels[FATAL]} )) && echo "$(date) FATAL $*"; }
log_error() { (( _log_level >= ${_log_levels[ERROR]} )) && echo "$(date) ERROR $*"; }
log_warning() { (( _log_level >= ${_log_levels[WARNING]} )) && echo "$(date) WARNING $*"; }
log_info() { (( _log_level >= ${_log_levels[INFO]} )) && echo "$(date) INFO $*"; }
log_debug() { (( _log_level >= ${_log_levels[DEBUG]} )) && echo "$(date) DEBUG $*"; }
log_verbose() { (( _log_level >= ${_log_levels[VERBOSE]} )) && echo "$(date) VERBOSE $*"; }
# functions for logging command output
log_debug_file() { (( _log_level >= ${_log_levels[DEBUG]} )) && [[ -f $1 ]] && echo "=== command output start ===" && cat "$1" && echo "=== command output end ==="; }
log_verbose_file() { (( _log_level >= ${_log_levels[VERBOSE]} )) && [[ -f $1 ]] && echo "=== command output start ===" && cat "$1" && echo "=== command output end ==="; }
Nehmen wir an, die obige Quelle befindet sich in einer Bibliotheksdatei namens "logging_lib.sh", wir könnten sie auf diese Weise in einem regulären Shell-Skript verwenden:
#!/bin/bash
source /path/to/lib/logging_lib.sh
set_log_level DEBUG
log_info "Starting the script..."
# method 1 of controlling a command's output based on log level
log_execute INFO date
# method 2 of controlling the output based on log level
date &> date.out
log_debug_file date.out
log_debug "This is a debug statement"
...
log_error "This is an error"
...
log_warning "This is a warning"
...
log_fatal "This is a fatal error"
...
log_verbose "This is a verbose log!"
Führt zu dieser Ausgabe:
Fri Feb 24 06:48:18 UTC 2017 INFO Starting the script...
Fri Feb 24 06:48:18 UTC 2017
=== command output start ===
Fri Feb 24 06:48:18 UTC 2017
=== command output end ===
Fri Feb 24 06:48:18 UTC 2017 DEBUG This is a debug statement
Fri Feb 24 06:48:18 UTC 2017 ERROR This is an error
Fri Feb 24 06:48:18 UTC 2017 ERROR This is a warning
Fri Feb 24 06:48:18 UTC 2017 FATAL This is a fatal error
Wie wir sehen können, log_verbose
hat keine Ausgabe erzeugt, da die Protokollebene DEBUG ist, eine Ebene unter VERBOSE. Jedoch log_debug_file date.out
hat die Ausgabe erzeugt und log_execute INFO
auch , da die Protokollebene auf DEBUG gesetzt ist, was>=INFO.
Auf dieser Grundlage könnten wir auch Befehls-Wrapper schreiben, wenn wir noch mehr Feinabstimmung benötigen:
git_wrapper() {
# run git command and print the output based on log level
}
Wenn diese vorhanden sind, könnte das Skript erweitert werden, um ein Argument --log-level level
zu akzeptieren die die Ausführlichkeit des Protokolls bestimmen kann, mit der es ausgeführt werden sollte.
Hier ist eine vollständige Implementierung des Loggings für Bash, reich an mehreren Loggern:
https://github.com/codeforester/base/blob/master/lib/stdlib.sh
Wenn jemand neugierig ist, warum einige Variablen im obigen Code mit einem führenden Unterstrich benannt sind, siehe diesen Beitrag:
- Korrigieren Sie die Großschreibung von Bash- und Shell-Skriptvariablen
Sie haben bereits die scheinbar sauberste Idee in Ihrer Frage (eine Wrapper-Funktion), aber Sie scheinen zu denken, dass dies chaotisch wäre. Ich würde vorschlagen, dass Sie es sich noch einmal überlegen. Es könnte wie folgt aussehen (nicht unbedingt eine vollwertige Lösung, nur um Ihnen die Grundidee zu vermitteln):
#!/bin/bash
# Argument 1 : Logging level for that command
# Arguments 2... : Command to execute
# Output suppressed if command level >= current logging level
log()
{
if
(($1 >= logging_level))
then
"${@:2}" >/dev/null 2>&1
else
"${@:2}"
fi
}
logging_level=2
log 1 command1 and its args
log 2 command2 and its args
log 3 command4 and its args
Sie können dafür sorgen, dass jede erforderliche Umleitung (mit Dateideskriptoren, wenn Sie möchten) in der Wrapper-Funktion behandelt wird, sodass der Rest des Skripts lesbar und frei von Umleitungen und Bedingungen bleibt, abhängig von der ausgewählten Protokollierungsstufe.
Lösung 1. Erwägen Sie die Verwendung zusätzlicher Dateideskriptoren. Leiten Sie erforderliche Dateideskriptoren je nach ausgewählter Ausführlichkeit auf STDOUT oder /dev/null um. Leiten Sie die Ausgabe jeder Anweisung in Ihrem Skript auf einen Dateideskriptor um, der ihrer Wichtigkeit entspricht. unix.stackexchange.com/a/218355 .