bash 脚本中详细模式的优雅方式?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8455991/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Elegant way for verbose mode in scripts?
提问by tamasgal
When I write bash scripts I usually get the verbose mode this way (simplified):
当我编写 bash 脚本时,我通常会以这种方式(简化)获得详细模式:
_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.
有没有办法让它更优雅?我正在考虑定义一个名为“verbose”的函数并输入它而不是[ $_V -eq 1 ]
,但这只是一个微小的改进。
I'm sure, there is more common way to do it…
我敢肯定,有更常见的方法来做到这一点......
回答by ДМИТРИЙ МАЛИКОВ
As you noticed, you can define some log
functions like log
, log_debug
, log_error
, etc.
当你发现,你可以定义一些log
类似的功能log
,log_debug
,log_error
,等。
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"
log "some text"
If _V
(global variable) is equal 1
"some text" will be printed, in other case it will not.
如果_V
(全局变量)等于1
“某些文本”将被打印,在其他情况下则不会。
回答by fentas
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=
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
回答by Petrucci
#!/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
回答by Ben
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).
如果您想避免每次要记录某些内容时都执行“if”语句,则可以尝试这种方法(我就是这样做的)。
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.
这个想法是,而不是调用log
,而是调用$echoLog
。因此,如果您处于详细模式,$echoLog
则只会是echo
,但在非详细模式下,它是一个不打印任何内容并忽略参数的函数。
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 "" in
-v|--verbose) echoLog='echo'; ;;
esac
shift;
done
Now, you can just drop lines like $echoLog "Doing something verbose log worthy"
anywhere you want.
现在,您可以$echoLog "Doing something verbose log worthy"
随心所欲地放下线条。
回答by ata
A first try at a more flexible system with verbosity levels (Bash 4):
第一次尝试更灵活的系统,具有详细级别(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
After initialization, anywhere in your code you can use things like:
初始化后,您可以在代码中的任何位置使用以下内容:
! (( $# )) && _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.
等等。
回答by tamasgal
I also came up with this function to do a quick ifelse:
我还想出了这个函数来做一个快速的 ifelse:
function verbose () {
[[ $_V -eq 1 ]] && return 0 || return 1
}
This executes a command if $_V is set to 1. Use it like this:
如果 $_V 设置为 1,这将执行命令。像这样使用它:
verbose && command #command will be executed if $_V == 1
or
或者
verbose && command -v || command # execute 'command -v' if $_V==1, else execute 'command'
回答by user123444555621
verbose=false
while getopts "v" OPTION
do
case $OPTION in
v) verbose=true
;;
esac
done
Then
然后
$verbose && echo "Verbose mode on" || echo "Verbose mode off"
This will execute /bin/true
or /bin/false
, returning 0 or 1 respectively.
这将执行/bin/true
or /bin/false
,分别返回 0 或 1。
回答by anthony
To avoid using multiple if statements or using a variable to hold a function name how about declaring different functions based on the verbosity!
为了避免使用多个 if 语句或使用变量来保存函数名称,如何根据详细程度声明不同的函数!
This works for ALL bourne shell derivatives not just bash!
这适用于所有 bourne shell 衍生物,而不仅仅是 bash!
#verbose=verbose_true # uncomment to make script verbose
if [ "$verbose" ]; then
log() { echo "$@"; }
else
log() { :; }
fi
log This Script is Verbose
NOTE: the use of "verbose=verbose_true" makes script tracing a lot nicer but you can make that one if however you like.
注意:“verbose=verbose_true”的使用使脚本跟踪更好,但如果您愿意,也可以进行跟踪。
回答by fraff
I would propose a modified version of @fentas's answer:
我会提出@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=
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 "##代码##[$$]" -- "$*"
fi
fi
}