bash - read - ¿Manera elegante para el modo detallado en los scripts?
scripts on linux (9)
Como notó, puede definir algunas funciones de log
como log
, log_debug
, log_error
, etc.
function log () {
if [[ $_V -eq 1 ]]; then
echo "$@"
fi
}
Puede ayudar a aumentar la legibilidad del código principal y ocultar la función show / nonshow logic into logging.
log "some text"
Si _V
(variable global) es igual a 1
, se imprimirá "algún texto", en otro caso no lo será.
Cuando escribo scripts de bash, normalmente obtengo el modo detallado de esta manera (simplificado):
_V=0
while getopts "v" OPTION
do
case $OPTION in
v) _V=1
;;
esac
done
y luego cada vez que quiero una "salida detallada" escribo esto:
[ $_V -eq 1 ] && echo "verbose mode on" || echo "verbose mode off"
o por ejemplo esto:
[ $_V -eq 1 ] && command -v || command
¿Hay alguna manera de hacerlo más elegante? Estaba pensando en definir una función llamada "verbose" y escribirla en lugar de [ $_V -eq 1 ]
, pero esto solo sería una pequeña mejora.
Estoy seguro, hay una forma más común de hacerlo ...
Después de leer todas las otras publicaciones se me ocurrió esto
# set verbose level to info
__VERBOSE=6
declare -A LOG_LEVELS
# https://en.wikipedia.org/wiki/Syslog#Severity_level
LOG_LEVELS=([0]="emerg" [1]="alert" [2]="crit" [3]="err" [4]="warning" [5]="notice" [6]="info" [7]="debug")
function .log () {
local LEVEL=${1}
shift
if [ ${__VERBOSE} -ge ${LEVEL} ]; then
echo "[${LOG_LEVELS[$LEVEL]}]" "$@"
fi
}
Entonces simplemente puedes usarlo así
# verbose error
.log 3 "Something is wrong here"
Que saldrá
[error] Something is wrong here
Para evitar el uso de varias declaraciones if o el uso de una variable para mantener un nombre de función, ¿qué hay de declarar diferentes funciones basadas en la verbosidad?
¡Esto funciona para TODOS los derivados de la concha de bourne, no solo bash!
#verbose=verbose_true # uncomment to make script verbose
if [ "$verbose" ]; then
log() { echo "$@"; }
else
log() { :; }
fi
log This Script is Verbose
NOTA: el uso de "verbose = verbose_true" hace que el seguimiento del script sea mucho más agradable, pero puede hacerlo si lo desea.
Propondría una versión modificada de la @fentas de @fentas :
# set verbose level to info
__VERBOSE=6
declare -A LOG_LEVELS
# https://en.wikipedia.org/wiki/Syslog#Severity_level
LOG_LEVELS=([0]="emerg" [1]="alert" [2]="crit" [3]="err" [4]="warning" [5]="notice" [6]="info" [7]="debug")
function .log () {
local LEVEL=${1}
shift
if [ ${__VERBOSE} -ge ${LEVEL} ]; then
if [ -t 0 ]; then
# seems we are in an interactive shell
echo "[${LOG_LEVELS[$LEVEL]}]" "$@" >&2
else
# seems we are in a cron job
logger -p "${LOG_LEVELS[$LEVEL]}" -t "$0[$$]" -- "$*"
fi
fi
}
Si desea evitar hacer una declaración "si" cada vez que quiera registrar algo, puede probar este enfoque (que es como lo hago).
La idea es que en lugar de llamar al log
, llame a $echoLog
en $echoLog
lugar. Por lo tanto, si está en modo detallado, $echoLog
solo será echo
, pero en modo no detallado, es una función que no imprime nada y simplemente ignora los argumentos.
Aquí hay un código que puedes copiar.
# Use `$echoLog` everywhere you print verbose logging messages to console
# By default, it is disabled and will be enabled with the `-v` or `--verbose` flags
declare echoLog=''silentEcho''
function silentEcho() {
:
}
# Somewhere else in your script''s setup, do something like this
while [[ $# > 0 ]]; do
case "$1" in
-v|--verbose) echoLog=''echo''; ;;
esac
shift;
done
Ahora, solo puede colocar líneas como $echoLog "Doing something verbose log worthy"
cualquier lugar que desee.
También se me ocurrió esta función para hacer un rápido ifelse:
function verbose () {
[[ $_V -eq 1 ]] && return 0 || return 1
}
Esto ejecuta un comando si $ _V se establece en 1. Úselo así:
verbose && command #command will be executed if $_V == 1
o
verbose && command -v || command # execute ''command -v'' if $_V==1, else execute ''command''
Un primer intento en un sistema más flexible con niveles de verbosidad (Bash 4):
# CONFIG SECTION
# verbosity level definitions
config[verb_levels]=''debug info status warning error critical fatal''
# verbosity levels that are to be user-selectable (0-this value)
config[verb_override]=3
# user-selected verbosity levels (0=none, 1=warnings, 2=warnings+info, 3=warning+info+debug)
config[verbosity]=2
# FUNCTION DEFINITIONS SECTION
_messages() {
# shortcut functions for messages
# non overridable levels exit with errlevel
# safe eval, it only uses two (namespaced) values, and a few builtins
local verbosity macro level=0
for verbosity in ${config[verb_levels]}; do
IFS="" read -rd'''' macro <<MACRO
_$verbosity() {
$( (( $level <= ${config[verb_override]} )) && echo "(( /${config[verbosity]} + $level > ${config[verb_override]} )) &&" ) echo "${verbosity}: /$@";
$( (( $level > ${config[verb_override]} )) && echo "exit $(( level - ${config[verb_override]} ));" )
}
MACRO
eval "$macro"
(( level++ ))
done
}
# INITIALIZATION SECTION
_messages
Después de la inicialización, en cualquier parte de tu código puedes usar cosas como:
! (( $# )) && _error "parameter expected"
[[ -f somefile ]] && _warning "file $somefile already exists"
_info "some info"
_status "running command"
if (( ${config[verbosity]} <= 1 )); then
command
else
command -v
fi
# explicitly changing verbosity at run time
old_verbosity=${config[verbosity]}
config[verbosity]=1
etc.
#!/bin/bash
# A flexible verbosity redirection function
# John C. Petrucci (http://johncpetrucci.com)
# 2013-10-19
# Allows your script to accept varying levels of verbosity flags and give appropriate feedback via file descriptors.
# Example usage: ./this [-v[v[v]]]
verbosity=2 #Start counting at 2 so that any increase to this will result in a minimum of file descriptor 3. You should leave this alone.
maxverbosity=5 #The highest verbosity we use / allow to be displayed. Feel free to adjust.
while getopts ":v" opt; do
case $opt in
v) (( verbosity=verbosity+1 ))
;;
esac
done
printf "%s %d/n" "Verbosity level set to:" "$verbosity"
for v in $(seq 3 $verbosity) #Start counting from 3 since 1 and 2 are standards (stdout/stderr).
do
(( "$v" <= "$maxverbosity" )) && echo This would display $v
(( "$v" <= "$maxverbosity" )) && eval exec "$v>&2" #Don''t change anything higher than the maximum verbosity allowed.
done
for v in $(seq $(( verbosity+1 )) $maxverbosity ) #From the verbosity level one higher than requested, through the maximum;
do
(( "$v" > "2" )) && echo This would not display $v
(( "$v" > "2" )) && eval exec "$v>/dev/null" #Redirect these to bitbucket, provided that they don''t match stdout and stderr.
done
# Some confirmations:
printf "%s/n" "This message is seen at verbosity level 3 and above." >&3
printf "%s/n" "This message is seen at verbosity level 4 and above." >&4
printf "%s/n" "This message is seen at verbosity level 5 and above." >&5
verbose=false
while getopts "v" OPTION
do
case $OPTION in
v) verbose=true
;;
esac
done
Entonces
$verbose && echo "Verbose mode on" || echo "Verbose mode off"
Esto ejecutará /bin/true
o /bin/false
, devolviendo 0 o 1 respectivamente.