Bash变量包含一组值中的值

时间:2013-03-15 01:04:05

标签: bash

我正在开发一个“包装脚本”,用作Bash中的“日志记录辅助工具”。

它应该在调用时打印出有关调用堆栈的信息。

我已经完成了它的工作,接下来,但仍有一些问题/疑问,我想从这里的专家那里得到最好的答案。


我的代码:

################################################################################
# Formats a logging message.                                                    

function my_function_format_logging_message() {                                 

  local -r TIMESTAMP="$(date '+%H:%M:%S')"                                      
  local -r PROCESS="$$" # Deliberately not using $BASHPID, focus: parent process

  local -r CALLER="${FUNCNAME[1]}"                                              

  local -i call_stack_position=1                                                
  if [[ "${CALLER}" == 'my_function_log_trace' ||                               
        "${CALLER}" == 'my_function_log_debug' ||                               
        "${CALLER}" == 'my_function_log_info' ||                                
        "${CALLER}" == 'my_function_log_warning' ||                             
        "${CALLER}" == 'my_function_log_error' ||                               
        "${CALLER}" == 'my_function_log_critical' ]]                            
  then                                                                          
    call_stack_position=$((call_stack_position++))                              
  fi                                                                            

  local -r SOURCE="$(basename "${BASH_SOURCE[$call_stack_position]}")"          
  local -r FUNCTION="${FUNCNAME[$call_stack_position]}"                         
  local -r LINE="${BASH_LINENO[$call_stack_position-1]}" # Previous function    

  local -r SEVERITY="$1"                                                        
  local -r MESSAGE="$2"                                                         

  # TODO: perform argument validation                                           

  printf '%s [PID %s] %s %s %s:%s - %s\n' \                                     
         "${TIMESTAMP}" \                                                       
         "${PROCESS}" \                                                         
         "${SEVERITY}" \                                                        
         "${SOURCE}" \                                                          
         "${FUNCTION}" \                                                        
         "${LINE}" \                                                            
         "${MESSAGE}"                                                           

}                                                                               
################################################################################

用法示例:

my_function_format_logging_message CRITICAL Temporarily increasing energy level to 9001

或:

my_function_log_info Dropping back to power level 42

我的疑惑:

  • call_stack_position = $((call_stack_position ++))

我想不出更好的方法来增加这个变量,是否有更好/更易读的形式?

  • 我可以使用更好的构造来检测调用是否是由记录方法进行的吗? (例如trace,debug,info ..)。所有这些if陈述都让我的眼睛受伤了。

  • 我是否重新发明轮子/滥用我想学习的工具? (即shell脚本)

我当然可能正在重新发明轮子,但这是自我训练..有一天我不再是一个收费站的夜班工人了。


注意

我正在寻找与指定的my_function_log_ *名称和没有其他人的匹配项。假设我有这种自由度是不行的(许多if就是出于这个原因,我正在寻找一些语法糖或更好地使用语言功能来做那种“集合成员”检验)。

3 个答案:

答案 0 :(得分:2)

我可以为你的前两个问题提出建议:

if [[ "${CALLER}" == my_function_log_* ]]
then 
  let call_stack_position++
fi

如果您只想在log _:

之后获得一组值
if [[ "${CALLER}" =~ my_function_log_(trace|debug|info|warning|error|critical) ]]
then 
  let call_stack_position++
fi      

答案 1 :(得分:1)

更可读的增量方法是在数字上下文中递增它:

(( call_stack_position++ ))

对于匹配,你可以在bash中使用glob:

[[ $CALLER == my_function_log_* ]]

就重新发明轮子而言,您可以使用logger命令从bash使用syslog日志记录。本地syslog守护程序将处理格式化日志消息并将其写入文件。

logger -p local0.info "CRITICAL Temporarily increasing energy level to 9001"

根据评论更新。您可以使用关联数组更明确地了解您要查找的内容。它需要bash v4或更高版本。

declare -A arr=(
    ['my_function_log_trace']=1
    ['my_function_log_debug']=1
    ['my_function_log_info']=1
    ['my_function_log_warning']=1
    ['my_function_log_error']=1
    ['my_function_log_critical']=1
);

if [[ ${arr[CALLER]} ]]; then
    ...
fi

您还可以使用扩展globbing进行模式匹配,类似于perreal的答案中的正则表达式,但没有正则表达式:

shopt -s extglob
if [[ $CALLER == my_function_log_@(trace|debug|info|warning|error|critical) ]]; then
    ...
fi

答案 2 :(得分:1)

Bash的类型系统,如果你甚至想要调用它,那么它是非常基本的:字符串和整数是它唯一的一等公民,数组是事后补充的,它的功能远不及Python集或Ruby数组。话虽如此,对于依赖于字符串匹配的数组,有一个穷人的in运算符。给定一系列函数名称:

log_functions=(my_function_log_trace my_function_log_debug my_function_log_info my_function_log_warning my_function_log_error my_function_log_critical)

这样:

[[ ${log_functions[*]} =~ \\b$CALLER\\b ]]

将仅匹配数组的成员。当我们谈论穷人的构造时,你可以将上面的模式与布尔控制算子结合成一个穷人的三元分配,完全跳过数值评估:

local -i call_stack_position=$([[ ${log_functions[*]} =~ \\b$CALLER\\b ]] && echo 1 || echo 2)
systems that do not support the GNU extensions to regcomp() (notably OS X and Cygwin)上的

警告:,字边界匹配需要使用更冗长的字符类形式,即

[[ ${log_functions[*]} =~ [[:\<:]]$CALLER[[:\>:]] ]]

注意:看到你的代码并注意到你提到你正在学习shell脚本,我提供两个与正确问题无关的观察结果:

  1. 变量扩展的括号表示法仅用于数组访问,扩展操作以及在字符串连接中消除var名称的歧义。在其他情况下,即在您的测试和printf命令中都不需要它。
  2. 使用扩展字符串操作比使用外部组件快得多,因此建议尽可能使用。不要使用basename,而是使用${var##*/}