Элегантный способ для подробного режима в сценариях? - PullRequest
26 голосов
/ 10 декабря 2011

Когда я пишу сценарии bash, я обычно получаю подробный режим следующим образом (упрощенно):

_V=0

while getopts "v" OPTION
do
  case $OPTION in
    v) _V=1
       ;;
  esac
done

, а затем каждый раз, когда я хочу получить "подробный вывод", я печатаю это:

[ $_V -eq 1 ] && echo "verbose mode on" || echo "verbose mode off"

или, например, это:

[ $_V -eq 1 ] && command -v || command

Есть ли способ сделать это более элегантно?Я думал об определении функции с именем «verbose» и набрал ее вместо [ $_V -eq 1 ], но это было бы лишь незначительным улучшением.

Я уверен, что есть более распространенный способ сделать это…

Ответы [ 9 ]

33 голосов
/ 10 декабря 2011

Как вы заметили, вы можете определить некоторые log функции, такие как log, log_debug, log_error и т. Д.

function log () {
    if [[ $_V -eq 1 ]]; then
        echo "$@"
    fi
}

Это может помочь улучшить читабельность основного кода и скрыть логику показа \ nonshow в функции регистрации.

log "some text"

Если _V (глобальная переменная) равна 1, будет напечатан «некоторый текст», в противном случае - нет.

15 голосов
/ 08 ноября 2015

Прочитав все остальные посты, я придумал это

# 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
}

Тогда вы можете просто использовать это так

# verbose error
.log 3 "Something is wrong here"

Который будет выводить

[error] Something is wrong here
5 голосов
/ 06 января 2014
#!/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
1 голос
/ 23 сентября 2014

Если вы хотите избегать выполнения оператора «if» каждый раз, когда хотите что-то записать, вы можете попробовать этот подход (как я это делаю).

Идея состоит в том, что вместо вызоваlog, вместо этого вы звоните $echoLog.Итак, если вы находитесь в подробном режиме, $echoLog будет просто echo, но в не подробном режиме это функция, которая ничего не печатает и просто игнорирует аргументы.

Вот код, который вы можете скопировать.

# 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

Теперь вы можете просто отбрасывать строки, как $echoLog "Doing something verbose log worthy", в любом месте.

1 голос
/ 10 декабря 2011

Первая попытка более гибкой системы с уровнями детализации (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

После инициализации в любом месте вашего кода вы можете использовать такие вещи, как:

! (( $# )) && _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

и т.д.

1 голос
/ 10 декабря 2011

Я также придумал эту функцию, чтобы сделать быстрый ifelse:

function verbose () {
    [[ $_V -eq 1 ]] && return 0 || return 1
}

Это выполняет команду, если $ _V установлен в 1. Используйте это так:

verbose && command #command will be executed if $_V == 1

или

verbose && command -v || command # execute 'command -v' if $_V==1, else execute 'command'
0 голосов
/ 04 октября 2017

Я бы предложил модифицированную версию ответа @ 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
}
0 голосов
/ 15 ноября 2016

Чтобы не использовать несколько операторов if или использовать переменную для хранения имени функции, как насчет объявления различных функций на основе многословия!

Это работает для ВСЕХ производных оболочки Борна, а не только для bash!

#verbose=verbose_true       # uncomment to make script verbose
if [ "$verbose" ]; then
  log() { echo "$@"; }
else
  log() { :; }
fi

log This Script is Verbose

ПРИМЕЧАНИЕ: использование "verbose = verbose_true" делает трассировку скрипта намного приятнее но вы можете сделать это, если хотите.

0 голосов
/ 10 декабря 2011
verbose=false

while getopts "v" OPTION
do
  case $OPTION in
    v) verbose=true
       ;;
  esac
done

Тогда

$verbose && echo "Verbose mode on" || echo "Verbose mode off"

Это выполнит /bin/true или /bin/false, возвращая 0 или 1 соответственно.

...