awk将key = value对解析为变量

时间:2015-03-17 20:20:47

标签: bash awk

我需要定期查看一堆不同类型的文件,它们的共同点是这些行有一堆key=value类型的字符串。如下所示:

Version=2 Len=17 Hello Var=Howdy Other

我希望能够直接从awk中引用这些名称...所以类似于:

cat some_file | ... | awk '{print Var, $5}' # prints Howdy Other

我该怎么做呢?

5 个答案:

答案 0 :(得分:8)

你可以得到的最接近的是每行首先将变量解析为关联数组。也就是说,

awk '{ delete vars; for(i = 1; i <= NF; ++i) { n = index($i, "="); if(n) { vars[substr($i, 1, n - 1)] = substr($i, n + 1) } } Var = vars["Var"] } { print Var, $5 }'

更可读:

{
  delete vars;                   # clean up previous variable values
  for(i = 1; i <= NF; ++i) {     # walk through fields
    n = index($i, "=");          # search for =
    if(n) {                      # if there is one:

                                 # remember value by name. The reason I use
                                 # substr over split is the possibility of
                                 # something like Var=foo=bar=baz (that will
                                 # be parsed into a variable Var with the
                                 # value "foo=bar=baz" this way).
      vars[substr($i, 1, n - 1)] = substr($i, n + 1)
    }
  }

  # if you know precisely what variable names you expect to get, you can
  # assign to them here:
  Var     = vars["Var"]
  Version = vars["Version"]
  Len     = vars["Len"]
}
{
  print Var, $5                  # then use them in the rest of the code
}

答案 1 :(得分:3)

$ cat file | sed -r 's/[[:alnum:]]+=/\n&/g' | awk -F= '$1=="Var"{print $2}'
Howdy Other

或者,避免无用的猫:

$ sed -r 's/[[:alnum:]]+=/\n&/g' file | awk -F= '$1=="Var"{print $2}'
Howdy Other

如何运作

  • sed -r 's/[[:alnum:]]+=/\n&/g'

    这会将每个键值对放在自己的行上。

  • awk -F= '$1=="Var"{print $2}'

    这将读取键值对。由于字段分隔符选择为=,因此键最终为字段1,值为字段2.因此,我们只查找第一个字段为Var的行并打印相应的值。

答案 2 :(得分:3)

由于评论中的讨论清楚地表明纯粹的bash解决方案也是可以接受的:

#!/bin/bash
#      ^-- must be /bin/bash, not /bin/sh
#
# ...must also be bash 4.0 or newer.

while read -r -a words; do                # iterate over lines of input
  declare -A vars=( )                  # refresh variables for each line
  set -- "${words[@]}"                 # update positional parameters
  for word; do
    if [[ $word = *"="* ]]; then       # if a word contains an "="...
       vars[${word%%=*}]=${word#*=}    # ...then set it as an associative-array key
    fi
  done
  echo "${vars[Var]} $5"              # Here, we use content read from that line.
done <<<"Version=2 Len=17 Hello Var=Howdy Other"

<<<"Input Here"也可能是<file.txt,在这种情况下,文件中的行会被迭代。

如果您想使用$Var代替${vars[Var]},请用printf -v "${word%%=*}" %s "${word*=}"代替vars[${word%%=*}]=${word#*=},并删除其他地方对vars的引用。请注意,这不能像在关联数组方法那样清理输入行之间的变量。

答案 3 :(得分:0)

我将尝试向您解释一种非常通用的方式来实现此目的,如果您想打印出其他内容,可以轻松进行调整。

假设您有一个字符串,其格式如下:

key1=value1 key2=value2 key3=value3

或更通用的

key1_fs2_value1_fs1_key2_fs2_value2_fs1_key3_fs2_value3

使用fs1fs2两个不同的字段分隔符

您想使用这些值进行选择或某些操作。为此,最简单的方法是将它们存储在关联数组中:

array["key1"] => value1
array["key2"] => value2
array["key3"] => value3
array["key1","full"] => "key1=value1"
array["key2","full"] => "key2=value2"
array["key3","full"] => "key3=value3"

这可以通过awk中的以下功能来完成:

function str2map(str,fs1,fs2,map,   n,tmp) {
   n=split(str,map,fs1)
   for (;n>0;n--) { 
     split(map[n],tmp,fs2);
     map[tmp[1]]=tmp[2]; map[tmp[1],"full"]=map[n]
     delete map[n]
   }
}

因此,在处理完字符串之后,您将具有完全的灵活性,可以按照自己喜欢的任何方式进行操作:

awk '
    function str2map(str,fs1,fs2,map,   n,tmp) {
       n=split(str,map,fs1)
       for (;n>0;n--) { 
         split(map[n],tmp,fs2);
         map[tmp[1]]=tmp[2]; map[tmp[1],"full"]=map[n]
         delete map[n]
       }
    }
    { str2map($0," ","=",map) }
    { print map["Var","full"] }
   ' file

此方法的优点是您可以轻松地修改代码以打印您感兴趣的任何其他键,甚至可以根据以下示例进行选择:

(map["Version"] < 3) { print map["var"]/map["Len"] }

答案 4 :(得分:-1)

我知道这特别是关于awk但是提到这一点,因为很多人来到这里寻求分解name = value对的解决方案(使用/不使用awk)。

我发现以下方式简单直接,并且在管理多个空格/逗号方面非常有效 -

来源:http://jayconrod.com/posts/35/parsing-keyvalue-pairs-in-bash

change="foo=red bar=green baz=blue"

#use below if var is in CSV (instead of space as delim)
change=`echo $change | tr ',' ' '`

for change in $changes; do
    set -- `echo $change | tr '=' ' '`
    echo "variable name == $1  and variable value == $2"
    #can assign value to a variable like below
    eval my_var_$1=$2;
done