Elegant way for verbose mode in scripts?

27,606

Solution 1

As you noticed, you can define some log functions like log, log_debug, log_error, etc.

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

It can help increasing your main code readability and hide show\nonshow logic into logging function.

log "some text"

If _V(global variable) is equal 1 "some text" will be printed, in other case it will not.

Solution 2

After reading all other posts I came up with this

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

Then you can simply use it like this

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

Which will output

[error] Something is wrong here

Solution 3

#!/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

Solution 4

I also came up with this function to do a quick ifelse:

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

This executes a command if $_V is set to 1. Use it like this:

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

or

verbose && command -v || command # execute 'command -v' if $_V==1, else execute 'command'

Solution 5

If you want to avoid doing an "if" statement every single time you want to log something, you can try this approach (which is how I do it).

The idea is that instead of calling log, you call $echoLog instead. So, if you are in verbose mode, $echoLog will just be echo, but in non-verbose mode, it is a function that prints nothing and just ignores the arguments.

Here's some code you can copy.

# 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

Now, you can just drop lines like $echoLog "Doing something verbose log worthy" anywhere you want.

Share:
27,606
tamasgal
Author by

tamasgal

My name is Tamás Gál, I am an astroparticle physicist and working at Erlangen Centre for Astroparticle Physics (ECAP). I currently develop online monitoring and live reconstruction algorithms for the KM3NeT neutrino telescopes and maintain the IT services of KM3NeT and ECAP. My DevOps engineering skills include Docker (+Swarm), GitLab CI/CD, Jenkins, Xen, OpenVZ, Ansible and more than two decades of experience with Linux and BSD as system administrator. I spend most of my time with science (astroparticle physics), coding (Julia, Python, …) and electronics (both analog/digital); the rest preferably off the road with one of my motorbikes. I have an obsession to repair things and keep them alive as long as possible, no matter if it requires a gearbox restoration of my BMW R1100 GS or replacing a dead 0201 SMD capacitor on a MacBook logicboard. Furthermore, I love making music and spend a lot of time on my DIY modular synthesizer. #SOreadytohelp

Updated on April 10, 2020

Comments

  • tamasgal
    tamasgal about 4 years

    When I write bash scripts I usually get the verbose mode this way (simplified):

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

    and then every time I want a "verbose output" I type this:

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

    or for example this:

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

    Is there a way to do it more elegant? I was thinking about defining a function named "verbose" and type it instead of [ $_V -eq 1 ], but this would only be a tiny improvement.

    I'm sure, there is more common way to do it…