有条件地将行添加为列bash脚本

时间:2015-05-30 18:54:13

标签: linux bash awk sed

我一直在尝试编写一个正确格式化命令输出的bash脚本。输出将多列作为单个记录列表:

host="host1"
Disk Agent="A.06.20"
General Media Agent="A.06.20"
host="host2"
Disk Agent="A.06.20"
General Media Agent="A.06.20"
host="host3"
Disk Agent="A.06.20"
host="host4"
Disk Agent="A.06.20"
General Media Agent="A.06.20"

我想让脚本格式化为:

host="host1",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host2",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host3",Disk Agent="A.06.20",
host="host4",Disk Agent="A.06.20",General Media Agent="A.06.20"

正如您所看到的,并非每个主机都拥有全部3个值,因此它不能只迭代列表。

我的输出中有数百台主机,并且命令没有创建表或报告的选项非常令人沮丧。

输出中还有一堆其他的垃圾,我已经能够解决了,但我对sed和awk很新,所以它让我头疼。

谢谢!

3 个答案:

答案 0 :(得分:2)

这是一个sed脚本:

sed '/host/{:loop; N; /\nhost/!s/\n/,/; t loop; P; D}' foo.txt

它通过匹配主机,然后附加下一行来工作。如果下一行没有以host开头,则用\ n代替逗号。当您到达下一个“主机”行时,循环终止。 P命令在\ n之前打印多行模式空间的一部分,并且D删除该部分并将控制转移到脚本的顶部,以便下一个“主”行成为当前行并且脚本再次启动。

哪个输出:

host="host1",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host2",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host3",Disk Agent="A.06.20"
host="host4",Disk Agent="A.06.20",General Media Agent="A.06.20"

答案 1 :(得分:1)

好悲伤。 UNIX的通用文本处理工具是awk,只需使用它:

awk '
/^host/ { if (rec) print rec; rec=sep=""} 
{ rec = rec sep $0; sep="," }
END { print rec }
' file
host="host1",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host2",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host3",Disk Agent="A.06.20"
host="host4",Disk Agent="A.06.20",General Media Agent="A.06.20"

或更普遍有用的是,请注意此版本在每个输出行上始终具有相同数量的逗号分隔字段,并处理丢失的任何输入行:

$ cat file
host="host1"
Disk Agent="A.06.20"
General Media Agent="A.06.20"
host="host2"
General Media Agent="A.06.20"
host="host3"
Disk Agent="A.06.20"
host="host4"
Disk Agent="A.06.20"
General Media Agent="A.06.20"

awk '
BEGIN { FS="="; OFS="," }
/^host/ { ++numRecs }
!($1 in fld2nr) { fld2nr[$1] = ++numFlds }
{ recs[numRecs,fld2nr[$1]] = $0 }
END {
    for (recNr=1; recNr<=numRecs; recNr++) {
        for (fldNr=1; fldNr<=numFlds; fldNr++) {
            printf "%s%s", recs[recNr,fldNr], (fldNr<numFlds?OFS:ORS)
        }
    }
}
' file
host="host1",Disk Agent="A.06.20",General Media Agent="A.06.20"
host="host2",,General Media Agent="A.06.20"
host="host3",Disk Agent="A.06.20",
host="host4",Disk Agent="A.06.20",General Media Agent="A.06.20"

答案 2 :(得分:0)

我整理了一个小脚本,可以满足您的需求,只需要很少的努力就可以了解您的需求。

你真正需要对awk做的唯一事情是切断当前行的名称和值,这可以通过使用:

来实现
name=$(awk -F'=' '{print $1}' <<< $line)
value=$(awk -F'=' '{print $2}' <<< $line)

-F参数设置分隔符,其中行应被标记化,print $1print $2然后打印第一个和第二个标记,此处为名称和值

剩下的所有工作只是比较字符串和编写输出,在这里你只输出真正存在的东西,所以你存储的内容如下:

        if [ "${name}" == "host" ]; then
                output_data
                host="${value}"
        elif [ "${name}" == "Disk Agent" ]; then
                disk_agent="${value}"
        elif [ "${name}" == "General Media Agent" ]; then
                general_agent="${value}"
        fi

并输出

        if [ -n "${host}" ]; then
                echo -n "host=${host}"
                if [ -n "${disk_agent}" ]; then
                        echo -n ",Disk Agent=${disk_agent}"
                fi
                if [ -n "${general_agent}" ]; then
                        echo -n ",General Media Agent=${general_agent}"
                fi
                echo
        fi

输出值后,不要忘记将变量重置为""字符串,否则在下一次迭代中,可能会输出旧值。