如何根据条件从数据块中获取特定数据

时间:2014-02-23 12:56:05

标签: bash sed awk

我有一个这样的文件:

[group]
enable = 0
name =  green
test = more

[group]
name  = blue
test = home

[group]
value = 48
name = orange
test = out

标签和=之间可能还有一个或多个空格/标签和值 每个街区的行数可能会谨慎 我希望拥有name,但前提是enable = 0

所以输出应该是:

blue
orange

以下是我设法创建的内容:

awk -v RS="group" '!/enable = 0/ {sub(/.*name[[:blank:]]+=[[:blank:]]+/,x);print $1}'
blue
orange

这有几个错误:

  1. 我无法将RS设置为[group],这都会导致RS="[group]"RS="\[group\]"失败。如果name或其他标签包含group,则会失败。
  2. 我不希望RS使用多个字符,因为这只是gnu awk
  3. 有人有其他建议吗? sedawk并且不使用长链命令。

7 个答案:

答案 0 :(得分:5)

如果您知道组总是用空行分隔,请将RS设置为空字符串:

$ awk -v RS="" '!/enable = 0/ {sub(/.*name[[:blank:]]+=[[:blank:]]+/,x);print $1}'
blue
orange

@devnull在他的回答中解释说GNU awk也接受RS中的正则表达式,所以如果它在自己的行上,你只能在[group]分割:

gawk -v RS='(^|\n)[[]group]($|\n)' '!/enable = 0/ {sub(/.*name[[:blank:]]+=[[:blank:]]+/,x);print $1}'

这确保我们不会像

那样分裂邪恶的名字
[group]
enable = 0
name =  [group]
name = evil
test = more

答案 1 :(得分:5)

您的问题似乎是:

  

我无法将RS设置为[group],这都失败了RS="[group]"和   RS="\[group\]"

话说:

RS="[[]group[]]"

应该产生预期的结果。

答案 2 :(得分:5)

在记录中有明确name = value语句的情况下,我想先用这些映射填充数组,例如:

map["<name>"] = <value>

然后只使用名称来引用我想要的值。在这种情况下:

$ awk -v RS= -F'\n' '
{
    delete map
    for (i=1;i<=NF;i++) {
        split($i,tmp,/ *= */)
        map[tmp[1]] = tmp[2]
    }
}
map["enable"] !~ /^0$/ {
    print map["name"]
}
' file
blue
orange

如果您的awk版本不支持删除整个数组,请将delete map更改为split("",map)

与使用RE和/或sub()等等相比,它使得解决方案更加健壮和可扩展,以备将来比较和/或打印其他字段的值。

答案 3 :(得分:3)

由于您有行分隔记录,因此应考虑将awk置于段落模式中。如果您必须测试[group]标识符,只需添加代码来处理它。这是一些应该满足您要求的示例代码。像:

一样运行
awk -f script.awk file.txt

script.awk的内容:

BEGIN {

    RS=""
}

{
    for (i=2; i<=NF; i+=3) {

        if ($i == "enable" && $(i+2) == 0) {

            f = 1
        }

        if ($i == "name") {

            r = $(i+2)
        }
    }
}

!(f) && r {

    print r
}

{
    f = 0
    r = ""
}

结果:

blue
orange

答案 4 :(得分:3)

这可能适合你(GNU sed):

sed -n '/\[group\]/{:a;$!{N;/\n$/!ba};/enable\s*=\s*0/!s/.*name\s*=\s*\(\S\+\).*/\1/p;d}' file

如果[group]变量未设置为enable,请将0块读入模式空间,然后替换颜色。

  • sed -n '...'设置sed以静默模式运行,除非指定为pP命令
  • 否则不输出
  • /\[group\]/{...}当我们有一行包含[group]时,请执行大括号内的内容。
  • :a;$!{N;/\n$/!ba}做一个循环我们需要一个循环的地方,:a是循环的地方。 $是文件地址的结尾,$!表示文件的结尾,因此$!{...}表示在花括号内找到的内容不是文件的结尾。 N表示当我们有一行以空行分支(/\n$/ba)到b结尾时,将换行符和下一行附加到当前行和a。因此,这会从包含“[group]”的行收集所有行到空行(或文件末尾)。
  • /enable\s*=\s*0/!s/.*name\s*=\s*\(\S\+\).*/\1/p如果收集的行包含enable = 0,则不要替换颜色。换句话说,如果到目前为止收集的行包含enable = 0 替换颜色。

答案 5 :(得分:2)

如果您不想使用记录分隔符,可以使用如下虚拟变量:

#!/usr/bin/awk -f

function endgroup() {
   if (e == 1) {
      print n
   }
}

$1 == "name" {
   n = $3
}

$1 == "enable" && $3 == 0 {
  e = 0;
}
$0 == "[group]" {
   endgroup();
   e = 1;
}

END {
   endgroup();
}

答案 6 :(得分:1)

你实际上可以使用Bash。

while read line; do
    if [[ $line == "enable = 0" ]]; then
        n=1
    else
        n=0
    fi
    if [ $n -eq 0 ] && [[ $line =~ name[[:space:]]+=[[:space:]]([a-z]+) ]]; then
        echo ${BASH_REMATCH[1]}
    fi
done < file

只有当enable = 0始终只有name行的一行时才会有效。