首页 > 解决方案 > 有没有办法读取由制表符分隔的键值对?

问题描述

我正在编写基于配置文件的构建环境,但在解析它们时遇到了困难。该环境包含尝试读取配置以进行相应设置的 Makefile (bmake) CFLAGS

配置文件保持简单,不需要特殊=符号:

ident               Appname           # E.g. core.S:15: .ident $ident$
cpu                 arch (amd64, x86) # -march=cpuarch -mtune=cpuarch

option              debug             # Enable debugging.
option              sanitize          # Sanitize undefined behaviour.
option              asyncio           # Asynchronous I/O.
option              ccpipe            # GCC/Clang -pipe option.

我无法找出正确的正则表达式来使用 , 或 解析grep这些sed选项awk。因为我很想用简单conditional statements的方式确定这些特征bmake,例如

DBG_ENABLED!= $read if option debug is set$ ? yes : no.
.if ${DBG_ENABLED} != ""} || ${DBG_ENABLED} == "yes" || ${DBG_ENABLED} != "no"
CFLAGS+=      -O -g
.else
CFLAGS+=      -O2
.endif

PIPE_ENABLED!= $read if option ccpipe is set$ ? yes : no.
.if ${PIPE_ENABLED} != "no"
CFLAGS+=      -pipe
.endif

那么如何通过shell命令确定设置了X例如选项option debug?我考虑过 grepping 文件或使用awk...

标签: shellawksedconfigurationgrep

解决方案


将配置文件读入关联数组:

$ declare -A opts="( $(awk -F'\t+' 'NF{print "["$1","$2"]=1"}' file) )"

$ for idx in "${!opts[@]}"; do echo "$idx=${opts[$idx]}"; done
cpu,arch (amd64, x86)=1
option,debug=1
option,asyncio=1
option,ccpipe=1
option,sanitize=1
ident,Appname=1

然后只是测试${opts["option,debug"]}是否被填充。

或者,如果您更愿意获得以下选项:

$ declare -A opts="( $(awk -F'\t+' '$1=="option"{print "["$2"]=1"}' file) )"

$ for idx in "${!opts[@]}"; do echo "$idx=${opts[$idx]}"; done
ccpipe=1
sanitize=1
asyncio=1
debug=1

无论您喜欢哪种语法:

$ if (( ${opts[debug]} )); then echo "do debug stuff"; else echo "nope, dont do it"; fi
do debug stuff

$ if (( ${opts[fluff]} )); then echo "do debug stuff"; else echo "nope, dont do it"; fi
nope, dont do it

$ if [[ -n ${opts[debug]} ]]; then echo "do debug stuff"; else echo "nope, dont do it"; fi
do debug stuff

$ if [[ -n ${opts[fluff]} ]]; then echo "do debug stuff"; else echo "nope, dont do it"; fi
nope, dont do it

更新:由于您的文件显然没有像您所说的那样真正以制表符分隔,因此删除注释然后删除所有剩余的前导/尾随空格并使用第一个剩余的空白链作为第一个字段和其余部分之间的分隔符该行(必须arch (amd64, x86)在第一个脚本中视为“字段”):

$ declare -A opts="( $(awk '{sub(/#.*/,""); gsub(/^[[:space:]]+|[[:space:]]+$/,"")} NF{k=$1; sub(/[^[:space:]]+[[:space:]]+/,""); print "["k","$0"]=1"}' file) )"

$ for idx in "${!opts[@]}"; do echo "$idx=${opts[$idx]}"; done
cpu,arch (amd64, x86)=1
option,debug=1
option,asyncio=1
option,ccpipe=1
option,sanitize=1
ident,Appname=1

$ declare -A opts="( $(awk '{sub(/#.*/,""); gsub(/^[[:space:]]+|[[:space:]]+$/,"")} $1=="option"{print "["$2"]=1"}' file) )"

$ for idx in "${!opts[@]}"; do echo "$idx=${opts[$idx]}"; done
ccpipe=1
sanitize=1
asyncio=1
debug=1

最后更新:所有事情都认为这可能是你真正想要的:

$ declare -A opts="( $(awk '
{
    sub(/#.*/,"")
    gsub(/^[[:space:]]+|[[:space:]]+$/,"")
}

NF && ($1 != "ident") {
    f1 = $1
    sub(/[^[:space:]]+[[:space:]]+/,"")
    f2 = $0
    if (f1 == "option") {
        idx = f2
        val = 1
    } else {
        idx = f1
        val = f2
    }
    print "[" idx "]=\"" val "\""
}
' file ) )"

$ for idx in "${!opts[@]}"; do echo "$idx=${opts[$idx]}"; done
ccpipe=1
sanitize=1
asyncio=1
debug=1
cpu=arch (amd64, x86)

推荐阅读