【问题标题】:Parsing variables from config file in Bash从 Bash 中的配置文件中解析变量
【发布时间】:2013-05-10 10:04:37
【问题描述】:

在文件中包含以下内容:

VARIABLE1="Value1"
VARIABLE2="Value2"
VARIABLE3="Value3"

我需要一个输出以下内容的脚本:

Content of VARIABLE1 is Value1
Content of VARIABLE2 is Value2
Content of VARIABLE3 is Value3

有什么想法吗?

【问题讨论】:

    标签: bash parsing variables awk


    【解决方案1】:

    由于您的配置文件是一个有效的 shell 脚本,您可以将其源到您当前的 shell 中:

    . config_file
    echo "Content of VARIABLE1 is $VARIABLE1"
    echo "Content of VARIABLE2 is $VARIABLE2"
    echo "Content of VARIABLE3 is $VARIABLE3"
    

    稍微干燥,但更棘手

    . config_file
    for var in VARIABLE1 VARIABLE2 VARIABLE3; do
        echo "Content of $var is ${!var}"
    done
    

    【讨论】:

    • @samson 一方面,它允许注入任何和所有恶意代码。
    • 为什么要担心从配置文件中注入恶意代码?我想我可以想象这种情况,但我需要能够从仅存在于我的本地计算机上的文件中执行此操作,即用户/密码组合列表...
    • 您需要在此答案中硬编码变量名称。有了公认的答案,你就没有,这就是让它变得更好的原因。
    • @samson,我会担心这一点,因为小安全漏洞变成大安全漏洞的方式是攻击者发现他们可以利用的漏洞来增加他们(通常最初是最小的)访问。一个不小心分配权限的配置文件往往就是这样的机会。
    • 我也更喜欢这里接受的解决方案,因为它适用于更一般的情况,并且它会自动独立于变量名和数量
    【解决方案2】:

    awk -F\= '{gsub(/"/,"",$2);print "Content of " $1 " is " $2}' <filename>

    仅供参考,另一种纯 bash 解决方案

    IFS="="
    while read -r name value
    do
    echo "Content of $name is ${value//\"/}"
    done < filename
    

    【讨论】:

    • 您也可以使用IFS="=" read -r name value来处理拆分(-r与拆分无关,但除非您有理由不使用,否则建议使用)。
    【解决方案3】:

    如果你需要这些...

    特点

    • 单行和内联 cmets;
    • 修剪= 周围的空格(即var = value 不会失败);
    • 引用的字符串值;
    • 理解 DOS 行尾;
    • 保持安全,避免采购配置文件。

    代码

    shopt -s extglob
    configfile="dos_or_unix" # set the actual path name of your (DOS or Unix) config file
    tr -d '\r' < $configfile > $configfile.unix
    while IFS='= ' read -r lhs rhs
    do
        if [[ ! $lhs =~ ^\ *# && -n $lhs ]]; then
            rhs="${rhs%%\#*}"    # Del in line right comments
            rhs="${rhs%%*( )}"   # Del trailing spaces
            rhs="${rhs%\"*}"     # Del opening string quotes 
            rhs="${rhs#\"*}"     # Del closing string quotes 
            declare $lhs="$rhs"
        fi
    done < $configfile.unix
    

    评论

    tr -d '\r' ... 删除 DOS 回车。
    ! $lhs =~ ^\ *# 跳过单行 cmets,-n $lhs 跳过空行。
    使用${rhs%%*( )} 删除尾随空格需要使用shopt -s extglob 设置扩展通配符。 (除了使用sed),您可以通过更复杂的方式避免这种情况:

    rhs="${rhs%"${rhs##*[^ ]}"}"  
    

    测试配置文件

    ## This is a comment 
    var1=value1             # Right side comment 
    var2 = value2           # Assignment with spaces 
    
    ## You can use blank lines 
    var3= Unquoted String   # Outer spaces trimmed
    var4= "My name is "     # Quote to avoid trimming 
    var5= "\"Bob\""         
    

    测试代码

    echo "Content of var1 is $var1"
    echo "Content of var2 is $var2"
    echo "Content of var3 is [$var3]"
    echo "Content of var4 + var5 is: [$var4$var5]"
    

    结果

    Content of var1 is value1
    Content of var2 is value2
    Content of var3 is [Unquoted String]
    Content of var4 + var5 is: [My name is "Bob"]
    

    【讨论】:

    • 不是 rhs="${rhs%\"}" 用于结束引号和 rhs="${rhs#\"}" 用于开始引号吗?
    • 还有另一个问题:当值为“aaaa#AAAAA”时,脚本会从 # 删除 rhs 上的所有内容
    • 如果值中有#,我可以确认此脚本失败。
    【解决方案4】:

    我是这样做的

    . $PATH_TO_FILE
    

    【讨论】:

    • 类似于 Dave Kennedy 所说的,这允许注入任何和所有恶意代码
    • 仅当有人更改“$PATH_TO_FILE”中的原始文件时。如果有人能做到这一点,那么,系统管理员马虎。
    【解决方案5】:
    awk '{print "Content of "$1" is "$3}' FS='[="]'
    

    结果

    VARIABLE1 的内容是 Value1 VARIABLE2 的内容是 Value2 VARIABLE3 的内容是 Value3

    【讨论】:

      【解决方案6】:
          # 
          #------------------------------------------------------------------------------
          # parse the ini like $0.$host_name.cnf and set the variables
          # cleans the unneeded during after run-time stuff. Note the MainSection
          # courtesy of : http://mark.aufflick.com/blog/2007/11/08/parsing-ini-files-with-sed
          #------------------------------------------------------------------------------
          doParseConfFile(){
              # set a default cnfiguration file
              cnf_file="$run_unit_bash_dir/$run_unit.cnf"
      
              # however if there is a host dependant cnf file override it
              test -f "$run_unit_bash_dir/$run_unit.$host_name.cnf" \
                  && cnf_file="$run_unit_bash_dir/$run_unit.$host_name.cnf"
      
              # yet finally override if passed as argument to this function
              # if the the ini file is not passed define the default host independant ini file
              test -z "$1" || cnf_file=$1;shift 1;
      
      
              test -z "$2" || ini_section=$2;shift 1;
              doLog "DEBUG read configuration file : $cnf_file"
              doLog "INFO read [$ini_section] section from config file"
      
              # debug echo "@doParseConfFile cnf_file:: $cnf_file"
              # coud be later on parametrized ...
              test -z "$ini_section" && ini_section='MAIN_SETTINGS'
      
              doLog "DEBUG reading: the following configuration file"
              doLog "DEBUG ""$cnf_file"
              ( set -o posix ; set ) | sort >"$tmp_dir/vars.before"
      
              eval `sed -e 's/[[:space:]]*\=[[:space:]]*/=/g' \
                  -e 's/#.*$//' \
                  -e 's/[[:space:]]*$//' \
                  -e 's/^[[:space:]]*//' \
                  -e "s/^\(.*\)=\([^\"']*\)$/\1=\"\2\"/" \
                  < $cnf_file \
                  | sed -n -e "/^\[$ini_section\]/,/^\s*\[/{/^[^#].*\=.*/p;}"`
      
              ( set -o posix ; set ) | sort >"$tmp_dir/vars.after"
      
              doLog "INFO added the following vars from section: [$ini_section]"
              cmd="$(comm -3 $tmp_dir/vars.before $tmp_dir/vars.after | perl -ne 's#\s+##g;print "\n $_ "' )"
              echo -e "$cmd"
              echo -e "$cmd" >> $log_file
              echo -e "\n\n"
              sleep 1; printf "\033[2J";printf "\033[0;0H" # and clear the screen
          }
          #eof func doParseConfFile
      

      【讨论】:

        【解决方案7】:

        给定一个配置文件如下:-

        [a]
        b=C
        d=E;rm t1
        [b]
        g=h
        

        以下单行代码将解析并保存值:-

        CFG=path-to-file; for ini in `awk '/^\[/' $CFG`;do unset ARHG;declare -A ARHG;while read A B;do ARHG[$A]=$B;echo "in section $ini, $A is equal to"  ${ARHG["$A"]};done < <(awk -F'=' '/\[/ {x=0} x==1 && $0~/=/ && NF==2 {print $1, $2} $0==INI {x=1}' INI="$ini" $CFG);declare -p ARHG;echo;done;printf "end loop\n\n";declare -p ARHG
        

        现在,让我们分解一下

        CFG=path-to-file;
        for ini in `awk '/^\[/' $CFG` # finds the SECTIONS (aka "ini")
        do 
          unset ARHG # resets ARHG 
          declare -A ARHG # declares an associative array
          while read A B
          do
            ARHG[$A]=$B
            echo "in section $ini, $A is equal to"  ${ARHG["$A"]}
          done < <(awk -F'=' '/\[/ {x=0} x==1 && $0~/=/ && NF==2 {print $1, $2} $0==INI {x=1}' INI="$ini" $CFG)
          # the awk splits the file into sections, 
          # and returns pairs of values separated by "="
          declare -p ARHG # displays the current contents of ARHG
          echo
        done
        printf "end loop\n\n"
        declare -p ARHG
        

        这允许我们保存值,而无需使用 eval 或反引号。 为了“真正干净”,我们可以删除行首和行尾的 [:space:],忽略“^#”行,并删除“等号”周围的空格。

        【讨论】:

        • 当然,使用“declare -A”需要最新版本的 bash。例如,“GNU bash,版本 3.2.57(1)-release (x86_64-apple-darwin14)”不包含“-A”参数...
        猜你喜欢
        • 2014-06-03
        • 1970-01-01
        • 2012-04-30
        • 1970-01-01
        • 1970-01-01
        • 2019-07-23
        • 2013-02-27
        • 1970-01-01
        相关资源
        最近更新 更多